簡體   English   中英

為什么在Clojure中的瞬態映射中插入1000 000個值會產生一個包含8個項目的映射?

[英]Why inserting 1000 000 values in a transient map in Clojure yields a map with 8 items in it?

如果我嘗試做1000 000 assoc! 在瞬態向量上,我將得到1000 000個元素的向量

(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!后返回一個新的(但仍然是瞬態的)映射assoc! 而不是使用你傳入的那個。

關於assoc!ClojureDocs頁面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個條目,瞬態映射就會將后備數據結構從一對對象( PersistentArrayMap )切換到一個哈希表( PersistentHashMap ),此時assoc! 返回一個新的引用,而不是只更新舊的引用。

最簡單的解釋來自Clojure文檔本身(強調我的):

瞬態支持一組並行的“更改”操作,后面跟着類似的名稱! - assoc !, conj! 除了返回值本身是瞬態的,它們與持久對應物做的事情相同。 請特別注意,瞬態不是設計為就地瞄准。 您必須在下一次調用中捕獲並使用返回值。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM