F#中的Seq.windowed返回一个序列,其中每个窗口都是一个数组。为什么每个窗口都作为数组(一个非常具体的类型)返回,而不是作为另一个序列或IList<'T>返回?例如,如果目的是传达窗口的项可以被随机访问,那么IList<'T>就足够了,但是数组说明了两件事:元素是可变的和随机访问的。如果您可以合理选择阵列,那么windowed与Seq.groupBy有什么不同呢?为什么后者(或类似的运算符)不也将组的成员作为数组返回?
我想知道这是简单的设计疏忽,还是有更深层次的合同原因?
发布于 2013-07-10 22:17:31
我不知道这背后的设计原则是什么。我认为这可能只是实现的一个偶然方面- 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-11-07 11:25:23
有几个想法。
首先,要知道在其当前版本中,Seq.windowed和Seq.groupBy在其实现中都使用了非惰性集合。windowed使用数组并返回数组。groupBy构建了一个Dictionary<'tkey, ResizeArray<'tvalue>>,但对此保密,并以seq而不是ResizeArray的形式返回组值。
从groupBy返回ResizeArrays不适用于其他任何内容,因此显然需要隐藏。另一种选择是返回数据的ToArray()。这将需要创建数据的另一个副本,这是一个缺点。而且没有太多的好处,因为你不知道你的组会有多大,所以你不会期望进行随机访问或任何其他特殊的数组支持的东西。因此,简单地包装在seq中似乎是一个很好的选择。
对于windowed来说,这是一个不同的故事。在本例中,您希望返回一个数组。为什么?因为您已经知道该数组将有多大,所以您可以安全地进行随机访问,或者更好的是,进行模式匹配。这是一个很大的好处。然而,缺点仍然存在-数据需要重新复制到每个窗口的新分配的数组中。
seq{1 .. 100} |> Seq.windowed 3 |> Seq.map (fun [|x; _; y|] -> x + y)仍然有一个悬而未决的问题-“但是我们不能通过在内部只使用真正的惰性seq来避免数组分配/复制的负面影响吗?这样做不是更符合‘seq的精神’吗?”这可能有点棘手(需要一些花哨的枚举数克隆?),但可以肯定的是,可能需要一些仔细的编码。不过,这也有一个很大的缺点。您需要在内存中缓存整个未假脱机的seq才能使其工作,这在某种程度上否定了懒惰做事情的整个目标。与列表或数组不同,多次枚举seq并不能保证产生相同的结果(例如,返回随机数的seq ),因此需要将返回的这些seq窗口的备份数据缓存到某个地方。当该窗口最终被访问时,您不能只是点击并重新枚举原始源seq -您可能会得到不同的数据,或者seq可能在不同的位置结束。这指出了在Seq.windowed中使用数组的另一个好处--只有windowSize元素需要一次保存在内存中。
发布于 2013-11-05 22:13:03
这当然是纯粹的猜测。我认为这与两个函数的实现方式有关。
如前所述,在Seq.groupBy中,组的长度是可变的,而在Seq.windowed中,组的大小是固定的。
因此,在Seq.windowed的实现中,使用固定大小的数组更有意义,而不是Seq.groupBy中使用的Generic.List,F#中的btw称为ResizeArray。
现在,尽管Array是可变的,但在F#代码和库中被广泛使用,F#为创建、初始化和操作数组提供语法支持,而ResizeArray在F#代码中使用不是很广泛,该语言除了类型别名之外没有提供语法支持,所以我认为这就是他们决定将其公开为Seq的原因。
https://stackoverflow.com/questions/17563956
复制相似问题