Warning: file_get_contents(/data/phpspider/zhask/data//catemap/5/ember.js/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dictionary 为什么在Clojure的瞬态映射中插入1000 000个值会产生一个包含8项的映射?_Dictionary_Vector_Collections_Clojure_Transient - Fatal编程技术网

Dictionary 为什么在Clojure的瞬态映射中插入1000 000个值会产生一个包含8项的映射?

Dictionary 为什么在Clojure的瞬态映射中插入1000 000个值会产生一个包含8项的映射?,dictionary,vector,collections,clojure,transient,Dictionary,Vector,Collections,Clojure,Transient,如果我尝试做1000 000assoc

如果我尝试做1000 000
assoc
(count
  (let [m (transient [])]
    (dotimes [i 1000000]
      (assoc! m i i)) (persistent! m)))
; => 1000000
另一方面,如果我对地图做同样的操作,它将只有8个项目

(count
  (let [m (transient {})]
    (dotimes [i 1000000]
      (assoc! m i i)) (persistent! m)))
; => 8

发生这种情况的原因是什么?

瞬态数据类型的操作不能保证返回与传入的引用相同的引用。有时,实现可能会决定在
assoc!之后返回一个新的(但仍然是暂时的)映射而不是使用您传入的代码

具有解释此行为的示例:

;; The key concept to understand here is that transients are 
;; not meant to be `bashed in place`; always use the value 
;; returned by either assoc! or other functions that operate
;; on transients.

(defn merge2
  "An example implementation of `merge` using transients."
  [x y]
  (persistent! (reduce
                (fn [res [k v]] (assoc! res k v))
                (transient x)
                y)))

;; Why always use the return value, and not the original?  Because the return
;; value might be a different object than the original.  The implementation
;; of Clojure transients in some cases changes the internal representation
;; of a transient collection (e.g. when it reaches a certain size).  In such
;; cases, if you continue to try modifying the original object, the results
;; will be incorrect.

;; Think of transients like persistent collections in how you write code to
;; update them, except unlike persistent collections, the original collection
;; you passed in should be treated as having an undefined value.  Only the return
;; value is predictable.
我想重复最后一部分,因为它非常重要:您传入的原始集合应被视为具有未定义的值。只有返回值是可预测的。

以下是代码的修改版本,可以按预期工作:

(count
  (let [m (transient {})]
    (persistent!
      (reduce (fn [acc i] (assoc! acc i i))
              m (range 1000000)))))

作为旁注,总是得到8的原因是Clojure喜欢使用
Clojure.lang.PersistentArrayMap
(由数组支持的映射)来处理包含8个或更少元素的映射。一旦你超过8,它就会切换到
clojure.lang.PersistentHashMap

user=> (type '{1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a})
clojure.lang.PersistentArrayMap
user=> (type '{1 a 2 a 3 a 4 a 5 a 6 a 7 a 8 a 9 a})
clojure.lang.PersistentHashMap

一旦超过8个条目,瞬态映射将备份数据结构从成对数组(
persistentarymap
)切换到哈希表(
PersistentHashMap
),此时
assoc返回一个新引用,而不仅仅是更新旧引用。

最简单的解释来自于引用本身(强调部分):

瞬态支持一组并行的“更改”操作,具有相似的名称,后跟!-助理!,康!除返回值本身是瞬时的外,它们与持久的对应项做相同的事情请特别注意,瞬变不是设计用于就地冲击的。您必须在下次通话中捕获并使用返回值。