Seq.windowed
返回一个序列,其中每个窗口都是一个数组。有没有理由将每个窗口作为一个数组(一个非常具体的类型)返回,而不是说另一个序列或IList<'T>
?例如,IList<'T>
就足够了,如果目的是传达窗口的项目可以随机访问但数组说两件事:元素是可变的并且可以随机访问。如果您可以合理化数组的选择,windowed
与Seq.groupBy
的区别如何?为什么后者(或同一个运算符)也不会将组的成员作为数组返回?
我想知道这只是一个设计疏忽还是存在一个更深层次的合同理由?
答案 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.windowed
和Seq.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
。