【问题标题】:Clojure: immutability and persistenceClojure:不变性和持久性
【发布时间】:2014-11-26 15:11:15
【问题描述】:
每本教科书都说 Clojure 数据结构是“不可变且持久的”。他们用不同的长度来解释这个概念,但到目前为止,我还没有弄清楚不变性和持久性之间的区别是什么。是否存在持久但可变的实体?还是不可变但不持久?
【问题讨论】:
标签:
clojure
persistence
immutability
【解决方案2】:
Purely Functional Data Structures by Chris Okasaki 引用了一篇文章 [1],其中似乎包含术语 persistent 的原始定义:
普通数据结构是短暂的,因为对结构进行更改会破坏旧版本,只留下新版本。 ... 如果数据结构支持访问多个版本,我们将其称为 persistent。如果可以访问所有版本但只能修改最新版本,则该结构是部分持久,如果可以访问和修改每个版本,则该结构是完全持久。
[1] James R. Driscoll、Neil Sarnak、Daniel D. Sleator 和 Robert E. Tarjan。使数据结构持久化。 计算机与系统科学杂志,38(1):86–124,1989 年 2 月。
【解决方案3】:
不可变意味着持久,但持久并不意味着不可变。所以你可以拥有一些持久但不是一成不变的东西。
可变和持久数据结构的一个例子是 Java 的 CopyOnWriteArrayList。
持久性确实不意味着共享结构,也没有说明性能。当然,共享结构和良好的性能都是非常理想的,并且都是由 Clojure 的持久数据结构提供的。但是很有可能创建没有结构共享和糟糕性能的东西(例如,参见CopyOnWriteArrayList ;-))但仍然持久。
【解决方案4】:
基本上不可变 == 不能更改,并且持久 == 不可变,具有共享结构。
如果我有一种无法更改数组的语言,那么数组就是不可变的。要“更改”数组,我必须创建一个新数组并将每个元素(要更改的元素除外)复制到新数组中。这使得任何更新 O(n),其中 n 是数组中的元素数。对于较大的 n,这显然是低效的。
另一方面,如果我使用持久数据结构而不是数组,那么新版本不会在每次“更改”数据结构时复制每个元素,而是与旧版本共享大部分相同的结构。
细节取决于结构,但通常会涉及到一棵树。如果树是平衡的,则替换元素意味着沿着从根到包含该元素的叶子的路径创建节点的新副本。其余节点与原始版本共享。该路径的长度为 O(n log(n))。由于节点大小为 O(1),因此整个操作需要 O(n log(n)) 时间和额外空间。
请注意,并非所有持久结构都有效地支持相同的操作。例如,在 Clojure 中,列表是单链表。您可以有效地在前面添加和删除元素,但仅此而已。另一方面,向量允许您有效地获取任何元素并在后面添加/删除元素。