【发布时间】:2010-04-13 05:27:42
【问题描述】:
我试图了解序列和列表之间的区别。
在 F# 中,两者之间有明显的区别。但是在 C# 中,我看到程序员将 IEnumerable 集合称为序列。是什么让 IEnumerable 成为一个序列,它返回一个对象以遍历集合?
也许真正的区别纯粹是在函数式语言中发现的?
【问题讨论】:
我试图了解序列和列表之间的区别。
在 F# 中,两者之间有明显的区别。但是在 C# 中,我看到程序员将 IEnumerable 集合称为序列。是什么让 IEnumerable 成为一个序列,它返回一个对象以遍历集合?
也许真正的区别纯粹是在函数式语言中发现的?
【问题讨论】:
不是真的 - 你倾向于随机访问一个列表,以及能够快速获得它的计数等。不可否认的链接列表不具有随机访问性质......但是它们没有实现 @ 987654321@。特定平台提供的设施与一般概念之间存在灰色地带。
序列(由IEnumerable<T> 表示)是只读的、只进的、一次一个项目,并且可能是无限的。当然,序列的任何一个实现也可能是一个列表(例如List<T>),但是当您将其视为一个序列时,您基本上可以(重复地)对其进行迭代,仅此而已。
【讨论】:
GetEnumerator(),都会返回一个新的迭代器。一个实现只允许调用一次是可行的,但是大多数实现允许重复迭代。
List<T> 实现只是在您每次调用GetEnumerator() 时在列表上创建一个新的“光标”。没有克隆。每次调用GetEnumerator() 时,另一种实现可以为您提供一个随机(可能是无限的)序列,而无需克隆。另一种实现(例如,对于 ConcurrentDictionary)可以在您每次调用 GetEnumerator 时拍摄快照。
我认为混淆可能是因为像List<T> 这样的集合实现了接口IEnumerable<T>。如果您通常具有子类型关系(例如,超类型 Shape 具有两个子类型 Rectangle 和 Circle),则可以将该关系解释为“is-a”层次结构。
这意味着说“Circle 是一个 Shape”是完全可以的,同样,人们会说“List<T> 是一个 IEnumerable<T>”即“列表是一个序列”。这是有道理的,因为列表是序列的一种特殊类型。一般来说,序列也可以是惰性生成和无限的(这些类型也不能是列表)。无法由列表生成的(完全有效的)序列示例如下所示:
// C# version // F# version
IEnumerable<int> Numbers() { let rec loop n = seq {
int i = 0; yield n
while (true) yield return i++; yield! loop(n + 1) }
} let numbers = loop(0)
对于 F# 也是如此,因为 F# list 类型也实现了 IEnumerable<T>,但函数式编程并没有那么强调面向对象的观点(以及启用“is a”的隐式转换) F# 中很少使用解释)。
【讨论】:
序列内容是按需计算的,因此您可以在不影响记忆的情况下实现例如无限序列。 所以在C#中你可以写一个序列,例如
IEnumerable<int> Null() {
yield return 0;
}
它将返回无限的零序列。 你可以写
int[] array = Null().Take(10).ToArray()
尽管序列是无限的,但它会占用 10*4 字节的内存。 如您所见,C# 确实有序列和集合之间的区别
【讨论】: