【问题标题】:Clojure: immutability and persistenceClojure:不变性和持久性
【发布时间】:2014-11-26 15:11:15
【问题描述】:

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

【问题讨论】:

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

标签: clojure persistence immutability


【解决方案1】:

不可变意味着值无法更改,持久性意味着如果值已存在于程序中,则复制该值的路径。 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]

了解 Clojure 的持久向量,pt。 1 => http://hypirion.com/musings/understanding-persistent-vector-pt-1

持久化数据结构 => https://en.wikipedia.org/wiki/Persistent_data_structure

持久数据结构和托管引用 => http://www.infoq.com/presentations/Value-Identity-State-Rich-Hickey

【讨论】:

    【解决方案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 中,列表是单链表。您可以有效地在前面添加和删除元素,但仅此而已。另一方面,向量允许您有效地获取任何元素并在后面添加/删除元素。

        【讨论】:

          猜你喜欢
          • 2017-01-15
          • 1970-01-01
          • 2011-10-18
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-07-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多