सबसे पहले, ध्यान दें कि यह व्यवहार किसी भी डिफ़ॉल्ट मान पर लागू होता है जो बाद में उत्परिवर्तित होता है (जैसे हैश और स्ट्रिंग्स), न कि केवल सरणियाँ।
TL; DR : Hash.new { |h, k| h[k] = [] }
यदि आप सबसे मुहावरेदार समाधान चाहते हैं तो इसका उपयोग करें और इसकी परवाह न करें।
क्या काम नहीं करता
Hash.new([])
काम क्यों नहीं करता
आइए अधिक गहराई से देखें कि Hash.new([])
काम क्यों नहीं करता है:
h = Hash.new([])
h[0] << 'a' #=> ["a"]
h[1] << 'b' #=> ["a", "b"]
h[1] #=> ["a", "b"]
h[0].object_id == h[1].object_id #=> true
h #=> {}
हम देख सकते हैं कि हमारी डिफ़ॉल्ट वस्तु का पुन: उपयोग और परिवर्तन किया जा रहा है (यह इसलिए है क्योंकि इसे एक और केवल डिफ़ॉल्ट मान के रूप में पारित किया गया है, हैश का नया, नया डिफ़ॉल्ट मान प्राप्त करने का कोई तरीका नहीं है), लेकिन कोई कुंजी या मान क्यों नहीं हैं सरणी में, h[1]
अभी भी हमें एक मूल्य देने के बावजूद ? यहाँ एक संकेत है:
h[42] #=> ["a", "b"]
प्रत्येक []
कॉल द्वारा लौटाया गया सरणी केवल डिफ़ॉल्ट मान है, जिसे हम इस समय बदल रहे हैं ताकि अब हमारे नए मूल्य शामिल हों। चूंकि <<
हैश को असाइन नहीं करता है (वहाँ कभी नहीं एक के बिना रूबी में काम किया जा सकता है =
वर्तमान † ), हम कभी नहीं हमारी वास्तविक हैश में कुछ भी रख दिया है। इसके बजाय हमें उपयोग करना होगा <<=
(जो कि <<
जैसा +=
है +
):
h[2] <<= 'c' #=> ["a", "b", "c"]
h #=> {2=>["a", "b", "c"]}
यह इस प्रकार है:
h[2] = (h[2] << 'c')
Hash.new { [] }
काम क्यों नहीं करता
का उपयोग करते हुए Hash.new { [] }
हल पुन: उपयोग और मूल डिफ़ॉल्ट मान परिवर्तनशील (के रूप में दिए गए ब्लॉक हर बार कहा जाता है, एक नई सरणी लौटने) की समस्या है, लेकिन नहीं काम समस्या:
h = Hash.new { [] }
h[0] << 'a' #=> ["a"]
h[1] <<= 'b' #=> ["b"]
h #=> {1=>["b"]}
क्या काम करता है
असाइनमेंट का तरीका
हम हमेशा उपयोग करने के लिए याद है <<=
, तो Hash.new { [] }
है एक व्यवहार्य समाधान है, लेकिन यह थोड़ा अजीब और गैर मुहावरेदार (मैं कभी नहीं देखा है है <<=
जंगली में प्रयुक्त)। यह भी सूक्ष्म कीड़े के लिए प्रवण है अगर<<
अनजाने में उपयोग किए ।
परस्पर चलने का तरीका
के लिए दस्तावेज़Hash.new
राज्यों (जोर मेरा अपना):
यदि कोई ब्लॉक निर्दिष्ट किया जाता है, तो इसे हैश ऑब्जेक्ट और कुंजी के साथ बुलाया जाएगा, और डिफ़ॉल्ट मान वापस करना चाहिए। आवश्यकता होने पर मूल्य को हैश में संग्रहीत करना ब्लॉक की जिम्मेदारी है ।
यदि हम <<
इसके बजाय उपयोग करना चाहते हैं तो हमें ब्लॉक के भीतर से हैश में डिफ़ॉल्ट मान संग्रहीत करना चाहिए <<=
:
h = Hash.new { |h, k| h[k] = [] }
h[0] << 'a' #=> ["a"]
h[1] << 'b' #=> ["b"]
h #=> {0=>["a"], 1=>["b"]}
यह प्रभावी रूप से हमारे व्यक्तिगत कॉल से असाइनमेंट को स्थानांतरित करता है (जो उपयोग करेगा <<=
) पास करने Hash.new
के लिए अनपेक्षित व्यवहार के बोझ को हटाते हुए, पास हो गया <<
।
ध्यान दें कि इस पद्धति और अन्य के बीच एक कार्यात्मक अंतर है: यह तरीका पढ़ने पर डिफ़ॉल्ट मान प्रदान करता है (जैसा कि असाइनमेंट हमेशा ब्लॉक के अंदर होता है)। उदाहरण के लिए:
h1 = Hash.new { |h, k| h[k] = [] }
h1[:x]
h1 #=> {:x=>[]}
h2 = Hash.new { [] }
h2[:x]
h2 #=> {}
अपरिवर्तनीय तरीका है
आप सोच रहे होंगे कि ठीक Hash.new([])
काम क्यों नहीं Hash.new(0)
करता है। कुंजी यह है कि रूबी में न्यूमेरिक्स अपरिवर्तनीय हैं, इसलिए हम स्वाभाविक रूप से उन्हें कभी-कभी उत्परिवर्तित नहीं करते हैं। यदि हमने अपने डिफ़ॉल्ट मान को अपरिवर्तनीय माना है, तो हम Hash.new([])
भी ठीक का उपयोग कर सकते हैं :
h = Hash.new([].freeze)
h[0] += ['a'] #=> ["a"]
h[1] += ['b'] #=> ["b"]
h[2] #=> []
h #=> {0=>["a"], 1=>["b"]}
हालाँकि, ध्यान दें ([].freeze + [].freeze).frozen? == false
। इसलिए, यदि आप यह सुनिश्चित करना चाहते हैं कि अपरिवर्तनीयता पूरी तरह से संरक्षित है, तो आपको नई वस्तु को फिर से स्थिर करने का ध्यान रखना चाहिए।
निष्कर्ष
सभी तरीकों में से, मैं व्यक्तिगत रूप से "अपरिवर्तनीय तरीका" पसंद करता हूं - सामर्थ्य आमतौर पर चीजों को बहुत सरल बनाने के लिए तर्क देता है। यह सब के बाद, एकमात्र तरीका है जिसमें छिपे या सूक्ष्म अप्रत्याशित व्यवहार की कोई संभावना नहीं है। हालांकि, सबसे आम और मुहावरेदार तरीका है "परस्पर मार्ग"।
एक तरफ अंतिम रूप में, हश डिफ़ॉल्ट मानों के इस व्यवहार को रूबी कोन्स में नोट किया गया है ।
But यह कड़ाई से सही नहीं है, instance_variable_set
इस तरह के तरीकों को बायपास किया जाता है, लेकिन वे मेटाप्रोग्रामिंग के लिए मौजूद होना चाहिए क्योंकि एल-मान =
गतिशील नहीं हो सकता है।