何时在 F# 中使用序列而不是列表?

Posted

技术标签:

【中文标题】何时在 F# 中使用序列而不是列表?【英文标题】:When to use a sequence in F# as opposed to a list? 【发布时间】:2012-06-04 13:34:03 【问题描述】:

我知道list 实际上包含值,sequence 是IEnumerable<T> 的别名。在实际的 F# 开发中,什么时候应该使用序列而不是列表?

以下是我可以看出何时序列会更好的一些原因:

在与其他需要的 .NET 语言或库交互时 IEnumerable<T>。 需要表示一个无限序列(在实践中可能并没有真正用处)。 需要惰性求值。

还有其他的吗?

【问题讨论】:

我发现无限序列非常有用和常见。例如,System.Random.Next() 已经是一个变相的“无限序列”。通常我想要一些能根据需要生成尽可能多的元素的东西。我最近在 F# 中编写了一个俄罗斯方块,并将块生成表示为一个无限序列:它将随着游戏的进行创建尽可能多的块。 @Dr_Asik 请注意,以这种方式生成的seq 会在您每次查看时产生不同的随机数。这显然可能是非确定性错误的来源...... 【参考方案1】:

list 更实用,对数学更友好。当每个元素相等时,2 个列表相等。

序列不是。

let list1 =  [1..3]
let list2 =  [1..3]
printfn "equal lists? %b" (list1=list2)

let seq1 = seq 1..3
let seq2 = seq 1..3
printfn "equal seqs? %b" (seq1=seq2)

【讨论】:

【参考方案2】:

您应该始终在公共 API 中公开Seq。在您的内部实现中使用ListArray

【讨论】:

是因为它们可以很好地与其他 .NET 语言配合使用吗?即因为 Seq 被视为 IEnumerable<T>? 不,因为良好的设计实践。公开尽可能多的信息,仅此而已。 好的,公平的评论,这也是 C# 代码的好习惯 - 例如,最好将函数定义为 IEnumerable 而不是更重的 List 我同意可变返回结构的上下文(例如 .NET 中的这个设计缺陷:msdn.microsoft.com/en-us/library/afadtey7.aspx)或可能用于其他 .NET 语言的 API,但我一般不同意,部分原因是seq 对并行性非常不利。【参考方案3】:

也喜欢seq时:

您不想同时将所有元素保存在内存中。

性能并不重要。

你需要在枚举前后做一些事情,例如连接到数据库并关闭连接。

你没有连接(重复Seq.append会堆栈溢出)。

首选list 时:

元素很少。

你会预先准备好并斩首很多。

seqlist 都不利于并行性,但这并不一定意味着它们也不好。例如,您可以使用其中之一来表示要并行完成的一小部分单独的工作项。

【讨论】:

“seq 和 list 都不适合并行性”:您能否详细说明为什么 seq 不适合并行性?那么,并行性有什么好处,仅限数组? @Dr_Asik 数组是最好的,因为您可以递归地细分它们并保持良好的参考位置。树是第二好的,因为您也可以细分它们,但参考的局部性不是很好。列表和序列不好,因为你不能细分它们。如果您将替代元素分包出去,那么您将获得最糟糕的参考位置。 Guy Steele 讨论了阻碍并行性的线性集合,尽管他只考虑工作和深度而不是局部性(又名 缓存复杂性)。 labs.oracle.com/projects/plrg/Publications/…【参考方案4】:

我认为您对何时选择Seq 的总结非常好。以下是一些额外的要点:

在编写函数时默认使用Seq,因为这样它们就可以与任何 .NET 集合一起使用 如果您需要像Seq.windowedSeq.pairwise 这样的高级功能,请使用Seq

我认为默认选择Seq是最好的选择,那么我什么时候会选择不同的类型呢?

当您需要使用 head::tail 模式进行递归处理时,请使用 List (实现一些标准库中没有的功能)

当您需要一个可以逐步构建的简单不可变数据结构时,请使用 List (例如,如果您需要在一个线程上处理列表 - 以显示一些统计信息 - 并在收到更多值(即来自网络服务)时同时继续在另一个线程上构建列表)

在处理短列表时使用List - 如果值通常表示空列表,列表是最好使用的数据结构,因为它在这种情况下非常有效

当您需要大量值类型时使用Array (数组将数据存储在平坦的内存块中,因此在这种情况下它们的内存效率更高)

当您需要随机访问或更高性能(和缓存局部性)时使用Array

【讨论】:

非常感谢 - 正是我所追求的。学习 F# 以找出为什么有这两个元素(列表和序列)可以为您提供相似的功能时,会感到困惑。 "当您需要一个简单的不可变数据结构时使用List [...],您可以逐步构建 [...]并同时继续在另一个线程上构建列表[...]"您能否详细说明您在此处的含义/它是如何工作的?谢谢。 @Noein 这个想法是您可以随时迭代列表(它们是不可变的),但您可以使用 x::xs 创建新列表,而不会破坏任何可能正在迭代 @987654335 的现有工作人员@【参考方案5】:

只有一点:SeqArray 在并行性方面优于 List

您有多种选择:来自 F# PowerPack 的PSeq、Array.Parallel 模块和Async.Parallel(异步计算)。由于其顺序性(head::tail 组合),列表对于并行执行来说很糟糕。

【讨论】:

这是一个很好的观点 - 我想到的场景是当您需要在一个线程上构建集合(即当您从某个服务接收值时)并从另一个线程使用它(即计算统计并显示)。我同意对于并行处理(当您已经在内存中拥有所有数据时),拥有ArrayPSeq 会好得多。 为什么说seq的并行性比list好? seq 由于它们的顺序性,对于并行执行也很糟糕......

以上是关于何时在 F# 中使用序列而不是列表?的主要内容,如果未能解决你的问题,请参考以下文章

何时更喜欢 JSON 而不是 XML?

何时使用 f:viewAction / preRenderView 与 PostConstruct?

何时使用 preRenderView 与 viewAction?

对指针的引用如何在C ++中完全起作用,以及何时需要它们(在链表的情况下)

Django rest 框架序列化程序返回一个列表而不是 json

为啥使用片段,以及何时使用片段而不是活动?