何时在 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
。在您的内部实现中使用List
和Array
。
【讨论】:
是因为它们可以很好地与其他 .NET 语言配合使用吗?即因为Seq
被视为 IEnumerable<T>
?
不,因为良好的设计实践。公开尽可能多的信息,仅此而已。
好的,公平的评论,这也是 C# 代码的好习惯 - 例如,最好将函数定义为 IEnumerableseq
对并行性非常不利。【参考方案3】:
也喜欢seq
时:
您不想同时将所有元素保存在内存中。
性能并不重要。
你需要在枚举前后做一些事情,例如连接到数据库并关闭连接。
你没有连接(重复Seq.append
会堆栈溢出)。
首选list
时:
元素很少。
你会预先准备好并斩首很多。
seq
和 list
都不利于并行性,但这并不一定意味着它们也不好。例如,您可以使用其中之一来表示要并行完成的一小部分单独的工作项。
【讨论】:
“seq 和 list 都不适合并行性”:您能否详细说明为什么 seq 不适合并行性?那么,并行性有什么好处,仅限数组? @Dr_Asik 数组是最好的,因为您可以递归地细分它们并保持良好的参考位置。树是第二好的,因为您也可以细分它们,但参考的局部性不是很好。列表和序列不好,因为你不能细分它们。如果您将替代元素分包出去,那么您将获得最糟糕的参考位置。 Guy Steele 讨论了阻碍并行性的线性集合,尽管他只考虑工作和深度而不是局部性(又名 缓存复杂性)。 labs.oracle.com/projects/plrg/Publications/…【参考方案4】:我认为您对何时选择Seq
的总结非常好。以下是一些额外的要点:
Seq
,因为这样它们就可以与任何 .NET 集合一起使用
如果您需要像Seq.windowed
或Seq.pairwise
这样的高级功能,请使用Seq
我认为默认选择Seq
是最好的选择,那么我什么时候会选择不同的类型呢?
当您需要使用 head::tail
模式进行递归处理时,请使用 List
(实现一些标准库中没有的功能)
当您需要一个可以逐步构建的简单不可变数据结构时,请使用 List
(例如,如果您需要在一个线程上处理列表 - 以显示一些统计信息 - 并在收到更多值(即来自网络服务)时同时继续在另一个线程上构建列表)
在处理短列表时使用List
- 如果值通常表示空列表,列表是最好使用的数据结构,因为它在这种情况下非常有效
当您需要大量值类型时使用Array
(数组将数据存储在平坦的内存块中,因此在这种情况下它们的内存效率更高)
当您需要随机访问或更高性能(和缓存局部性)时使用Array
【讨论】:
非常感谢 - 正是我所追求的。学习 F# 以找出为什么有这两个元素(列表和序列)可以为您提供相似的功能时,会感到困惑。 "当您需要一个简单的不可变数据结构时使用List
[...],您可以逐步构建 [...]并同时继续在另一个线程上构建列表[...]"您能否详细说明您在此处的含义/它是如何工作的?谢谢。
@Noein 这个想法是您可以随时迭代列表(它们是不可变的),但您可以使用 x::xs
创建新列表,而不会破坏任何可能正在迭代 @987654335 的现有工作人员@【参考方案5】:
只有一点:Seq
和 Array
在并行性方面优于 List
。
您有多种选择:来自 F# PowerPack 的PSeq、Array.Parallel 模块和Async.Parallel(异步计算)。由于其顺序性(head::tail
组合),列表对于并行执行来说很糟糕。
【讨论】:
这是一个很好的观点 - 我想到的场景是当您需要在一个线程上构建集合(即当您从某个服务接收值时)并从另一个线程使用它(即计算统计并显示)。我同意对于并行处理(当您已经在内存中拥有所有数据时),拥有Array
或PSeq
会好得多。
为什么说seq
的并行性比list
好? seq
由于它们的顺序性,对于并行执行也很糟糕......以上是关于何时在 F# 中使用序列而不是列表?的主要内容,如果未能解决你的问题,请参考以下文章
何时使用 f:viewAction / preRenderView 与 PostConstruct?
何时使用 preRenderView 与 viewAction?
对指针的引用如何在C ++中完全起作用,以及何时需要它们(在链表的情况下)