我是F#的C#开发人员,我明白.net中的字符串是不可变的。换句话说,每次修改字符串时,都会得到一个新的字符串实例。
对于像我这样的非功能性思维,第一个问题就是效率,我理解C#可变对象不是持久性的。因为字符串操作在大多数应用程序中通常是微不足道的。
我的问题是,这也是F#列表的情况吗?F#克隆每个列表的变化吗?例如,在过滤列表时,我是否创建了一个包含较少项目的新列表?
更新:我不是在比较.net字符串和列表。我将string命名为不可变对象的一个例子,并想知道F#是否为它的List提供了任何特殊处理。
这就是我所说的“persistent”。
答案 0 :(得分:4)
我认为Dustin Campbell's introduction在解释列表不变性方面做得非常出色。
在功能世界中,列表是不可变的。这意味着节点共享是可能的,因为原始列表永远不会改变。因为第一个列表以空列表结束,所以必须复制其节点才能将其最后一个节点指向第二个列表。在追加操作之后,我们的列表如下所示:
此时,你们中间可能会更加怀疑,“嗯, 这是一个非常有趣的理论,但你可以证明吗?“
没问题。
使用F#列表递归的知识,我们可以检索 结合的后半部分(内部列表从4开始)取 尾巴,尾巴,尾巴。 List.tl是F#的功能 提供了提取列表的尾部。
> let lastHalf = List.tl (List.tl (List.tl combined));; val lastHalf : int list > lastHalf;; val it : int list = [4; 5; 6]
最后,因为F#是.NET Framework的一等公民,我们 可以完全访问所有基类库。所以,我们可以使用 用于测试lastHalf的
Object.ReferenceEquals
方法 第二个确实是同一个例子。> System.Object.ReferenceEquals(lastHalf, second);; val it : bool = true
你有它。信不信由你,附加两个不可变的 列表实际上比附加更快,内存效率更高 可变列表,因为必须复制的节点更少。
答案 1 :(得分:1)
F#列表是不可变的。没有API来“修改”F#列表,因此不涉及克隆。像List.filter
这样的操作会创建一个新列表(如果可能的话,可能与原始列表共享一些结构)。
(我认为这就是所有语言中所有不可变对象的工作原理。)
参见例如
http://en.wikipedia.org/wiki/Persistent_data_structure
有一个很好的描述以及建议共享如何工作的图片。
答案 2 :(得分:1)
我认为Dan和Brian的引用应该直接回答你的问题。如果您想了解更多信息,还可以查看以下材料(本周在MSDN上发布),这些材料是为了向.NET开发人员解释F#和功能概念而编写的:
Tutorial: Working with Functional Lists介绍了F#列表,并展示了如何在C#中实现相同的概念,这是了解列表工作方式(以及哪些操作有效)的好方法。
Overview: Working with Immutable Data提供了有关F#不变性的更多背景信息 - 它不直接回答您的问题,但也可能是一个有用的资源。
答案 3 :(得分:0)
F#列表是链接列表。因此,当您创建新列表时,它只是将项目添加到现有列表并指向包含新项目的头部。原始列表仍指向创建列表时头部的项目。换句话说,它们都指向列表中不同位置的相同列表。
答案 4 :(得分:0)
理解函数式语言列表以理解Cons Cells
的最佳方式