2015-04-16 11 views
7

अगर मैं एक क्षणिक वेक्टर पर 1000 000 assoc! करने की कोशिश, मैं 1000 000 तत्वोंक्लोजर में एक क्षणिक मानचित्र में 1000 000 मूल्य डालने में 8 वस्तुओं के साथ एक नक्शा उत्पन्न होता है?

(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 

क्या ऐसा कोई कारण है?

उत्तर

19

क्षणिक डेटाटाइप ऑपरेशंस गारंटी नहीं देता है कि वे उसी संदर्भ को वापस लौटाएंगे जैसा कि पारित किया गया था। कभी-कभी कार्यान्वयन assoc! के बाद एक नया (लेकिन अभी भी क्षणिक) मानचित्र वापस करने का निर्णय ले सकता है में पारित

ClojureDocs page on assoc! एक nice example है कि इस व्यवहार बताते हैं:।

;; 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 प्रविष्टियां प्राप्त, अपने क्षणिक नक्शा एक hashtable (PersistentHashMap) को जोड़े (PersistentArrayMap) की एक सरणी, जिस बिंदु पर assoc! बजाय एक नया संदर्भ देता है बस को अद्यतन करने का से समर्थन डेटा संरचना स्विच पुराना वाला।

5

सरल व्याख्या Clojure documentation ही (जोर मेरा) से है:

यात्रियों समर्थन की बदलती 'के संचालन के लिए एक समानांतर सेट, के बाद इसी तरह के नाम के साथ! - assoc !, conj! इत्यादि। ये वही चीजें करते हैं जो उनके निरंतर समकक्षों को छोड़ते हैं, रिटर्न मूल्यों को छोड़कर खुद को क्षणिक होते हैं। विशेष रूप से ध्यान दें कि ट्रांजिस्टर को जगह में डालने के लिए डिज़ाइन नहीं किया गया है। आपको अगली कॉल में वापसी मूल्य को कैप्चर और उपयोग करना होगा।

संबंधित मुद्दे