2015-04-16 13 views
7

Ben geçici vektör 1000 000 assoc! yapmaya çalışırsanız, 1000 000 elemanlarıNeden Clojure'da geçici bir haritaya 1000 000 değer eklendiğinde, içinde 8 öğe bulunan bir harita çıkıyor?

diğer taraftan
(count 
    (let [m (transient [])] 
    (dotimes [i 1000000] 
     (assoc! m i i)) (persistent! m))) 
; => 1000000 

, bir harita ile aynı yaparsanız, sadece sahip olacak bir vektör elde edersiniz 8 öğe bunun içinde

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

Bunun olmasının bir nedeni var mı?

cevap

19

Geçici veri türü işlemleri, geçirilen ile aynı başvuruyu döndüreceklerini garanti etmemektedir. Bazen, uygulama, sizden birini kullanmak yerine, bir assoc! sonra yeni (ama yine de geçici) bir harita döndürmeye karar verebilir. geçirilen

ClojureDocs page on assoc! bu davranışı açıklayan bir nice example sahiptir. bu çok önemli çünkü

;; 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. 

bu son bölümünü tekrarlamak istiyorum: Eğer sho geçti orijinal koleksiyon undefined bir değere sahip olarak ele alınmalıdır. Sadece dönüş değeri tahmin edilebilir. İşte

beklendiği gibi çalıştığını Kodunuzdaki değiştirilmiş bir sürümü var: Bir yan not olarak

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

, her zaman olsun nedeni Clojure bir clojure.lang.PersistentArrayMap (haritayı kullanmak sever çünkü 8'dir 8 veya daha az eleman içeren haritalar için bir dizi ile desteklenir. 8'i geçtikten sonra clojure.lang.PersistentHashMap'a geçer. Eğer 8 girdileri geçmiş olsun

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 

, sizin geçici harita noktası assoc! sadece güncelleştirmek yerine yeni bir başvuru döndüren bir hashtable'a (PersistentHashMap), ikişer ikişer (PersistentArrayMap) bir dizi, gelen destek veri yapısını anahtarları eski olan.

5

basit açıklama Clojure documentation kendisi (vurgu benim) dan:

Geçici ardından benzer adlarla 'değişen' operasyonlarının bir paralel dizi destek! - Assoc !, conj! Bunlar, geri dönüş değerleri kendileri geçici olmak dışında, ısrarlı muadilleriyle aynı şeyleri yaparlar. Özellikle, geçişlerin yerinde basacak şekilde tasarlanmadığını unutmayın. Bir sonraki çağrıda geri dönüş değerini yakalayıp kullanmalısınız.

İlgili konular