10

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

我想知道这是否只是设计疏忽,还是阵列有更深层次的合同原因?

4

3 回答 3

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)

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

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

于 2013-07-10T14:17:31.467 回答
6

几个想法。

首先,要知道在他们当前的版本中,两者都在他们的实现Seq.windowedSeq.groupBy使用非惰性集合。 windowed使用数组并返回数组。 groupBy建立 a Dictionary<'tkey, ResizeArray<'tvalue>>,但将其保密并将组值返回为 aseq而不是ResizeArray.

返回ResizeArrays fromgroupBy不适合其他任何东西,因此显然需要隐藏。另一种选择是返回ToArray()数据。这将需要创建另一个数据副本,这是一个缺点。而且实际上并没有太大的好处,因为无论如何您都不知道您的组有多大,因此您不希望进行随机访问或任何其他特殊的数组启用。因此,简单地包装 aseq似乎是一个不错的选择。

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

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

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

于 2013-11-07T03:25:23.003 回答
1

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

如前所述,Seq.groupBy组的长度可变,而组Seq.windowed的大小固定。

因此,在实现中Seq.windowed,使用固定大小的数组更有意义,而不是Generic.List使用 in Seq.groupBy,在 F# 中称为ResizeArray.

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

于 2013-11-05T14:13:03.330 回答