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


82

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

这是一些可以使序列更好的原因:

  • 与需要.NET的其他.NET语言或库进行交互时 IEnumerable<T>
  • 需要表示一个无限的序列(在实践中可能并不真正有用)。
  • 需要懒惰的评估。

还有其他吗?


3
我发现无限序列非常有用且常见。例如,System.Random.Next()已经是变相的“无限序列”。通常,我想要的东西可以生成所需数量的元素。我最近用F#编写了一个俄罗斯方块,并以无限的顺序来表示块生成:它将根据游戏的进行创建所需的数量。
阿西克2012年

2
@Dr_Asik请注意,每次查看时,seq生成的那种方式都会产生不同的随机数。显然,这可能是不确定性错误的来源...
JD 2012年

Answers:


96

我认为您的何时选择摘要Seq很好。以下是一些其他要点:

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

我认为Seq默认情况下选择是最好的选择,那么我什么时候会选择其他类型?

  • 使用List时,你需要使用递归的处理head::tail模式
    (实现一些功能,不提供标准库)

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

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

  • Array在需要大量值类型集合时使用
    (数组将数据存储在平面内存块中,因此在这种情况下它们可以提高内存效率)

  • 使用Array时,你需要随机存取或更高的性能(和缓存局部性)


1
非常感谢-正是我所追求的。当学习F#来弄清楚为什么有这两个元素(列表和序列)为您提供相似功能时,这令人困惑。
dodgy_coder 2012年

3
List在需要简单的不可变数据结构时可以使用它,您可以逐步构建并同时在另一个线程上继续构建列表[...]”。意思是这里/如何运作?谢谢。
Narfanar 2015年

2
@Noein这个想法是,您始终可以遍历列表(它们是不可变的),但是您可以使用创建新列表,x::xs而不会破坏可能正在进行迭代的任何现有工作人员xs
Tomas Petricek 2015年

29

在以下情况下也更喜欢seq

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

  • 性能并不重要。

  • 您需要在枚举之前和之后执行某些操作,例如,连接到数据库并关闭连接。

  • 您没有串联(重复Seq.append将导致堆栈溢出)。

list在以下情况下更喜欢:

  • 元素很少。

  • 您会做很多假装和斩首。

无论是seq也不list是并行好的,但,这并不一定意味着他们是坏的。例如,您可以使用其中一个来表示一小撮要并行完成的独立工作项。


“ seq和list都不适合并行性”:您能解释为什么seq不适合并行性吗?那么,仅数组对并行性有什么好处?
阿西克

5
@Dr_Asik数组是最好的,因为您可以递归地细分它们并保留良好的引用位置。树木次之,因为您也可以将其细分,但参考位置不是很好。列表和序列很糟糕,因为您无法细分它们。如果耕种备用元素,则会获得最差的参考位置。盖伊·斯蒂尔(Guy Steele)讨论了阻碍并行性的线性集合,尽管他只考虑工作和深度,而不考虑局部性(又称缓存复杂性)。labs.oracle.com/projects/plrg/Publications/…–
JD

12

仅一小点:Seq并且ArrayList并行性要好。

您有几种选择:PSEQ从F#PowerPack中,Array.Parallel模块和Async.Parallel(异步计算)。由于列表具有顺序性质(head::tail组成),因此它对于并行执行非常不利。


这是一个好点-我想到的场景是当您需要在一个线程上构建集合(即,当您从某个服务接收值时)并在另一个线程中使用它(即,计算统计数据并显示它)。我同意对于并行处理(当您已经将所有数据存储在内存中时)而言,拥有ArrayPSeq要好得多。
Tomas Petricek'5

1
为什么说这seqlist并行性好?seq由于它们的顺序性质,它们也很不适合并行执行...
JD 2012年

7

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

顺序不是。

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)

在此处输入图片说明


5

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


那是因为它们可以与其他.NET语言很好地协作吗?即因为aSeq被视为a IEnumerable<T>
dodgy_coder

不,因为好的设计规范。公开尽可能多的信息,仅此而已。
2012年

好吧公平的评论,这也是C#代码的好习惯-例如,最好将函数定义为IEnumerable <T>而不是较重的List <T>。
dodgy_coder

1
我同意可变返回结构(例如.NET中的设计缺陷:msdn.microsoft.com/en-us/library/afadtey7.aspx)或可以从其他.NET语言中使用的API的情况,但我不赞成大致上同意,部分原因seq是因为并行性太差了。
JD 2012年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.