Seq.windowed
返回一个序列,其中每个窗口都是一个数组。为什么每个窗口都返回数组(一种非常具体的类型)而不是另一个序列或IList<'T>
?例如,IList<'T>
如果目的是表达窗口的项可以随机访问,则足够了,但是数组有两个含义:元素是可变和可随机访问的。如果您可以说明选择数组的理由,那么windowed
与Seq.groupBy
有何不同?为什么后者(或同类操作符)也不以数组的形式返回组的成员?我想知道这是否只是设计上的疏忽还是数组背后有更深层次的契约原因?
Seq.windowed
返回一个序列,其中每个窗口都是一个数组。为什么每个窗口都返回数组(一种非常具体的类型)而不是另一个序列或IList<'T>
?例如,IList<'T>
如果目的是表达窗口的项可以随机访问,则足够了,但是数组有两个含义:元素是可变和可随机访问的。如果您可以说明选择数组的理由,那么windowed
与Seq.groupBy
有何不同?为什么后者(或同类操作符)也不以数组的形式返回组的成员?我不知道这背后的设计原则是什么。我猜可能只是实现中的一个偶然方面,因为Seq.windowed
可以通过将项存储在数组中很容易地实现,而Seq.groupBy
可能需要使用一些更复杂的结构。
总的来说,我认为F# API要么使用'T[]
,如果使用数组是自然高效的实现方式,或者当数据源可能是无限的、惰性的或者实现必须显式将数据转换为数组时,返回seq<'T>
(此时可以让调用者处理)。
对于Seq.windowed
,我认为数组是一个很好的选择,因为你知道数组的长度,所以你很可能会使用索引。例如,假设prices
是日期-价格元组的序列(seq<DateTime * float>
),你可以这样写:
prices
|> Seq.windowed 5
|> Seq.map (fun win -> fst (win.[2]), Seq.averageBy snd win)
这个示例计算浮动平均值,并使用索引获取中间的日期。
总之,我没有关于设计理念的很好解释,但我对所做选择感到非常满意 - 它们似乎在函数的通常用例中运行得非常好。
几点思考:
首先,需要知道的是,在它们当前的版本中,Seq.windowed
和Seq.groupBy
都在它们的实现中使用非惰性集合。 windowed
使用数组并返回数组。 groupBy
建立了一个Dictionary<'tkey, ResizeArray<'tvalue>>
,但将其保密,并将组值作为seq
而不是ResizeArray
返回。
从groupBy
返回ResizeArray
与其他任何内容都不符,因此显然需要隐藏它。另一种选择是返回数据的ToArray()
。这将需要创建另一个数据副本,这是一个缺点。而且没有真正的好处,因为您事先不知道您的组会有多大,因此您不期望进行随机访问或数组启用的任何其他特殊操作。因此,简单地包装在seq
中似乎是一个不错的选择。
对于windowed
,情况就不同了。在这种情况下,您需要返回一个数组。 为什么? 因为您已经知道该数组的大小,所以可以安全地进行随机访问或更好的模式匹配。 这是一个巨大的优势。 然而,缺点仍然存在-每个窗口都需要将数据重新复制到新分配的数组中。
seq{1 .. 100} |> Seq.windowed 3 |> Seq.map (fun [|x; _; y|] -> x + y)
Seq.windowed
中使用数组的另一个优点 - 只需要一次在内存中保留windowSize
个元素。
groupBy
中执行相同的操作,其中组的成员也可以作为数组返回?换句话说:Seq.groupBy: seq<'Key * seq<'T []>>
。 - Atif Azizseq<'t>
中要添加额外的[]
?我怀疑你的意思是 'seq<'Key * 'T[]>`,但这会对无限序列造成失败,因为它需要无限大的数组,但你可以使用原始定义来处理无限序列。 - John PalmergroupBy
文档中指出“不应与大型或无限序列一起使用”,因为它“对原始序列的排序没有任何假设”。有序的groupBy
或groupAdjacent
的情况是不同的,因为它可以在不必完全遍历输入序列的情况下流式传输组。 - Atif Aziz