Clojure:不变性和持久性

Clojure:不变性和持久性,clojure,persistence,immutability,Clojure,Persistence,Immutability,每本教科书都说Clojure数据结构是“不可变和持久的”。他们用了不同的篇幅来解释这个概念,但到目前为止,我没有弄清楚不变性和持久性之间的区别。是否存在持久但可变的实体?或者不可变但不持久?不可变意味着该值不能更改,持久性意味着如果该值已存在于程序中,则复制该值的路径。Clojure将此作为其结构共享实现的一部分。如果数据不存在,则创建该数据。如果数据存在,新数据将建立在旧版本数据的基础上,而不会对其进行更改或删除 原子是持久的,但可以安全地变化 user> (def +a+ (atom

每本教科书都说Clojure数据结构是“不可变和持久的”。他们用了不同的篇幅来解释这个概念,但到目前为止,我没有弄清楚不变性和持久性之间的区别。是否存在持久但可变的实体?或者不可变但不持久?

不可变意味着该值不能更改,持久性意味着如果该值已存在于程序中,则复制该值的路径。Clojure将此作为其结构共享实现的一部分。如果数据不存在,则创建该数据。如果数据存在,新数据将建立在旧版本数据的基础上,而不会对其进行更改或删除

原子是持久的,但可以安全地变化

user> (def +a+ (atom 0))
#'user/+a+
user> @+a+
0
user> (swap! +a+ inc)
1
user> @+a+
1
瞬变是可变的,但应在突变后保持不变

user> (def t (transient []))
#'user/t
user> (conj! t 1)
#<TransientVector clojure.lang.PersistentVector$TransientVector@658ee462>
user> (persistent! t)
[1]
user>(定义t(瞬态[])
#'用户/t
用户>(conj!t 1)
#
用户>(持久!t)
[1]
理解Clojure的持久向量,pt。1 =>

持久数据结构=>

持久数据结构和托管引用=>
基本上不可变==不能更改,持久==不可变,具有共享结构

如果我有一种不能更改数组的语言,那么数组是不可变的。要“更改”数组,我必须创建一个新数组,并将每个元素(要更改的元素除外)复制到新数组中。这将进行任何更新O(n),其中n是数组中的元素数。这对于大型n

另一方面,如果我使用持久数据结构而不是数组,那么新版本与旧版本共享大部分相同的结构,而不是每次“更改”数据结构时都复制每个元素

细节取决于结构,但通常涉及到一棵树。如果树是平衡的,则替换元素意味着沿从根到包含元素的叶的路径创建节点的新副本。其余节点与原始版本共享。此路径的长度为O(n log(n))。由于节点大小为O(1),因此整个操作需要O(n log(n))时间和额外空间

请注意,并非所有持久性结构都有效地支持相同的操作。例如,在Clojure中,列表是单链接列表。您可以高效地在前端添加和删除元素,但仅此而已。另一方面,向量允许您高效地获取任何元素并在后面添加/删除元素。

Chris Okasaki的纯功能数据结构引用了一篇文章[1],其中似乎包含术语持久性的原始定义:

普通数据结构是短暂的,因为对结构进行更改会破坏旧版本,只留下新版本…如果数据结构支持对多个版本的访问,我们称之为持久数据结构。如果可以访问所有版本,但只能修改最新版本,则该结构是部分持久的;如果可以访问和修改每个版本,则该结构是完全持久的


[1] 詹姆斯·R·德里斯科尔、尼尔·萨纳克、丹尼尔·D·斯莱托和罗伯特·E·塔扬。使数据结构持久化。《计算机与系统科学杂志》,38(1):86–124,1989年2月。

不可变意味着持久,但持久并不意味着不可变。所以你可以有一些持久的东西,但不是不变的

可变和持久数据结构的一个例子是Java的


持久性并不意味着共享结构,也不意味着性能。当然,共享结构和良好的性能都是非常理想的,并且都是由Clojure的持久数据结构提供的。但是很有可能创建一些没有结构共享和糟糕性能的东西(例如,请参见
CopyOnWriteArrayList
),但仍然是持久的。

查找“持久数据结构”的定义可能会有所帮助-这是Clojure没有发明或创造的一般CS东西