[英]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.