4

我已经开始学习 Clojure 并且正在阅读有关结构共享的内容。我对以下情况感到困惑:以下 clojure 代码按以下定义的顺序在 REPL 中输入:

1) (def a [1 2 3])
2) (def b a)
3) (def a (conj a 4))
4) (def b (conj b 5))

在第 4 步之后,a 和 b 将共享前三个元素的结构,还是将在第 4 步执行时复制所有值?如果结构是共享的,Clojure 将如何返回索引 3 处的值?

这与Clojure中的结构共享有些相关,但我仍然感到困惑。任何形式的帮助将不胜感激。

4

1 回答 1

7

在问题文本中给出的示例中,根本没有发生结构共享。这是因为向量被实现为树,其中实际元素存储在大小为 32 的叶节点中(最终叶单独存储为向量的“尾部”——性能优化),分支节点同样是 32 路的. 因此,为了使结构共享发挥作用,需要一个足够大的向量:

;; a base vector:
(def v1 (vec (range 31)))

;; no structural sharing -- all elements are copied:
(def v2 (conj v1 31))

;; leftmost leaf of internal tree uses v2's tail as its internal array:
(def v3 (conj v2 32))

;; leftmost leaf shared with v3
(def v4 (conj v3 33))

通常,每当conj将对象添加到现有向量上时,新向量要么 (1) 与原始向量共享整个内部树,但有一个新的尾部,或者 (2) 在原始向量内部树的每个级别上与原始向量共享, 除了最右边的节点之外的所有节点(并且可能有一个比原始节点高一级的内部树)。(显然所有元素都与新向量共享。)

至于按索引查找值,在每种情况下都以相同的方式发生——向量不关心它们的结构是否恰好与其他向量共享(并且没有理由需要它们,因为它永远不会改变)。

于 2013-09-13T00:54:45.693 回答