为什么F#的Seq.windowed返回数组的seq

时间:2013-07-10 06:37:26

标签: f#

F#中的

Seq.windowed返回一个序列,其中每个窗口都是一个数组。有没有理由将每个窗口作为一个数组(一个非常具体的类型)返回,而不是说另一个序列或IList<'T>?例如,IList<'T>就足够了,如果目的是传达窗口的项目可以随机访问但数组说两件事:元素是可变的并且可以随机访问。如果您可以合理化数组的选择,windowedSeq.groupBy的区别如何?为什么后者(或同一个运算符)也不会将组的成员作为数组返回?

我想知道这只是一个设计疏忽还是存在一个更深层次的合同理由?

3 个答案:

答案 0 :(得分:7)

我不知道这背后的设计原则是什么。我想这可能只是实现的一个偶然方面 - 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)

该示例计算浮动平均值,并使用索引来获取中间的日期。

总之,我对设计原理并没有很好的解释,但我对所做的选择非常满意 - 它们似乎与函数的常用用例非常相配。

答案 1 :(得分:6)

一些想法。

首先,要知道在其当前版本中,Seq.windowedSeq.groupBy在其实现中都使用非惰性集合。 windowed使用数组并返回数组。 groupBy会构建一个Dictionary<'tkey, ResizeArray<'tvalue>>,但会保密,并将组值作为seq而不是ResizeArray返回。

ResizeArray返回groupBy不符合其他任何内容,因此显然需要隐藏。另一种方法是回馈ToArray()数据。这将需要创建另一个数据副本,这是一个缺点。并没有太大的好处,因为你事先并不知道你的团队有多大,所以你不期望随机访问或任何其他特殊的东西阵列启用。所以简单地用seq包裹似乎是个不错的选择。

对于windowed,这是一个不同的故事。在这种情况下,您需要一个数组。为什么?因为你已经知道这个数组有多大,所以你可以安全地进行随机访问,或者更好的是模式匹配。这是一个很大的好处。但缺点仍然是 - 需要将数据重新复制到每个窗口的新分配数组中。

seq{1 .. 100} |> Seq.windowed 3 |> Seq.map (fun [|x; _; y|] -> x + y)

还有一个悬而未决的问题 - “但是我们不能通过内部仅使用真正的懒惰seqs来避免数组分配/复制下行,并将它们作为返回它们吗?这不是更多的'seq精神' ?”这将是一种棘手的问题(需要一些奇特的克隆点计算器?),但可以肯定,可能需要一些仔细的编码。不过,这有一个巨大的缺点。你需要将整个未整理的seq缓存到内存中以使其工作,这种做法否定了懒惰地做事的整个目标。与列表或数组不同,多次枚举seq并不能保证产生相同的结果(例如返回随机数的seq),因此返回的这些seq窗口的后备数据需要在某处缓存。当最终访问该窗口时,您不能只是点击并重新枚举原始源seq - 您可能会返回不同的数据,或者seq可能会在不同的位置结束。这表明在Seq.windowed中使用数组的另一个好处 - 只需要将windowSize个元素保存在内存中。

答案 2 :(得分:1)

这当然是纯粹的猜测。我认为这与两种功能的实现方式有关。

如前所述,在Seq.groupBy中,这些群组的长度可变,而Seq.windowed群体的大小固定。

所以在Seq.windowed的实现中,使用固定大小的数组更有意义,而不是Generic.List中使用的Seq.groupBy,而f#中的btw被称为{{1} }}

现在对外界来说,ResizeArray虽然可变,但在F#代码和库中被广泛使用,而F#为创建,初始化和操作数组提供了语法支持,而Array并未广泛使用在F#代码中,除了类型别名之外,语言不提供语法支持,所以我认为这就是为什么他们决定将其公开为ResizeArray

相关问题