我已经开始学习Clojure并正在阅读有关结构共享的内容。我在以下场景中感到困惑: 以下定义的序列在REPL中输入以下clojure代码:
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处返回值?
这与Structural Sharing in Clojure有些关系,但我仍感到困惑。任何形式的帮助将不胜感激。
答案 0 :(得分:6)
在问题文本中给出的示例中,根本没有发生结构共享。这是因为向量是作为树实现的,其中实际元素存储在大小为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)与原始对象共享,在原始内部树的每个级别,除了最右边的节点之外的所有节点(并且可以具有比原始树高一级的内部树)。 (显然,原始矢量的所有元素都与新的共享元素。)
至于按索引查找值,它在每种情况下都以相同的方式发生 - 向量不关心它们的结构是否恰好与其他向量共享(并且没有理由他们需要鉴于它永远不会改变。)