1

在 Racket 或 SML 等函数式语言中,我们通常在递归调用中执行列表操作(模式匹配、列表追加、列表连接......)。但是,我不确定这些操作在函数式语言中的一般实现。列表中的创建、更新或删除元素等操作是否会返回列表的全新副本?我曾经在一本书中读到一个关于函数式编程缺点的例子;也就是说,每次更新数据库时,都会返回一个全新的数据库副本。

我质疑这个例子,因为 FP 中的数据本质上是不可变的,因此从现有列表创建列表不应该创建一个全新的副本。相反,新列表只是基于过滤条件的对其他列表中现有对象的不同引用集合。

例如, listA = [a,b,c]和 list B=[1,2,3],我创建了一个新列表,其中包含现有列表中的前两个元素,即C=[a,b,1,2]. 这个新列表只包含对a,b,fromA1,2from 的引用B。它不应该是新副本,因为数据是不可变的。

因此,要更新列表中的元素,只需要线性时间在列表中找到一个元素,创建一个新值并创建一个新列表,该列表与旧列表中的元素相同,但更新后的元素除外。要创建一个新列表,运行环境只需更新前一个元素的下一个指针。如果一个列表保存了非原子元素(即列表、树...),并且仅更新了其中一个非原子元素中的一个原子元素,则该过程递归地应用于非原子元素,直到原子元素如上所述更新。这是应该如何实施的吗?

如果有人在每次从现有列表/添加/更新/删除/带有元素创建列表时创建列表的整个深层副本,那么他们做错了,不是吗?

还有一点是,当程序环境更新时(即为一个新变量添加一个新的键/值条目,以便我们以后可以参考),它并没有违反函数式编程的不可变属性,是吗?

4

1 回答 1

2

你是绝对正确的!具有不可变数据的 FP 语言永远不会进行深度复制(除非它们的实现真的很差)。由于数据不可变的,因此重用它永远不会有任何问题。它与所有其他结构的工作方式完全相同。因此,例如,如果您正在使用树结构,那么最多只会复制实际的树,而不会复制其中包含的数据。

因此,虽然复制听起来非常昂贵,但如果您来自命令式/OO 背景(您确实必须复制,因为您有可变数据),它比您最初想象的要少得多。拥有不可变数据有很多好处。

于 2013-03-23T19:29:00.793 回答