जब आप किसी चीज़ का उतना ही उपयोग करते हैं जितना रूबी डेवलपर हैश का उपयोग करते हैं, तो यह सोचना आसान होता है कि आपने यह सब देख लिया है।
लेकिन मैं यहां आपको बता रहा हूं कि विनम्र माणिक हैश की आस्तीन में कुछ तरकीबें हैं। एक गूंगा कुंजी-मूल्य प्रणाली होने से दूर, हैश ऑब्जेक्ट आपको कुछ बहुत ही रोचक और परिष्कृत चीजें करने की शक्ति देता है।
कोई भी वस्तु हैश कुंजी हो सकती है
इससे पहले कि हम और आगे बढ़ें, मैं एक बात बताना चाहूँगा जो शायद स्पष्ट न हो। जबकि हम स्ट्रिंग्स और प्रतीकों का उपयोग हैश कुंजियों के रूप में करते हैं, इसका मतलब यह नहीं है कि हम अन्य प्रकार की वस्तुओं का भी उपयोग नहीं कर सकते हैं। वास्तव में, आप हैश कुंजी के रूप में लगभग किसी भी चीज़ का उपयोग कर सकते हैं।
# Numbers can be hash keys
{1 => "one"}[1] # "one"
# So can the Ruby kernel
{Kernel => 1}[Kernel] # 1
# You can store values for specific classes
{Kernel => 1, String => 2}["hello world".class] # 2
# You can store values for booleans
{true => "verdad"}[1==1] # "verdad"
# You can even use complex arrays and even other hashes as hash keys
{[[1,0],[0,1]] => "identity matrix"}[[[1,0], [0,1]]] # "identity matrix"
इनमें से कुछ विकल्प दूसरों की तुलना में अधिक उपयोगी हैं, लेकिन वे सभी आपके लिए उपलब्ध हैं।
आपका डिफ़ॉल्ट मान पर नियंत्रण होता है।
मान लें कि आपके पास हैश h={ a: 1 }
. है . अगर आप किसी ऐसे मान को एक्सेस करने का प्रयास करते हैं जो मौजूद नहीं है - उदाहरण के लिए h[:x]
- आपको शून्य मिलता है। ऐसा इसलिए है क्योंकि शून्य प्रत्येक हैश का डिफ़ॉल्ट मान है, जब तक कि आप अन्यथा निर्दिष्ट न करें।
आप एक नए हैश के लिए उसके कंस्ट्रक्टर में एक तर्क पास करके डिफ़ॉल्ट मान सेट कर सकते हैं।
h = Hash.new("This attribute intentionally left blank")
h[:a] = 1
h[:a] # 1
h[:x] # "This attribute intentionally left blank"
डायनामिक डिफ़ॉल्ट मान
ध्यान दें, क्योंकि यह चाल ही हर चीज का आधार है।
यदि आप कंस्ट्रक्टर में एक ब्लॉक पास करते हैं, तो आप प्रोग्रामेटिक रूप से डिफ़ॉल्ट मान उत्पन्न कर सकते हैं। नीचे दिए गए उदाहरण में मैंने डिफ़ॉल्ट मान में टाइमस्टैम्प जोड़ा है, ताकि आप देख सकें कि यह गतिशील रूप से जेनरेट किया जा रहा है।
h = Hash.new { |hash, key| "#{key}: #{ Time.now.to_i }" }
h[:a] # "a: 1435682937"
h[:a] # "a: 1435682941"
h[:b] # "b: 1435682943"
यह महत्वपूर्ण है क्योंकि "डिफ़ॉल्ट मान" ब्लॉक डिफ़ॉल्ट मान वापस करने के अलावा अन्य काम भी कर सकता है।
हैश कुंजी मौजूद न होने पर अपवाद उठाना
हैश के साथ मुख्य समस्याओं में से एक यह है कि वे चुपचाप विफल हो जाते हैं। आपने गलती से user[:phnoe]
. टाइप कर दिया है user[:phone]
. के बजाय , और अपवाद उठाने के बजाय, हैश शून्य लौटाता है। लेकिन आप इस व्यवहार को बदल सकते हैं।
h = Hash.new { |hash, key| raise ArgumentError.new("No hash key: #{ key }") }
h[:a]=1
h[:a] # 1
h[:x] # raises ArgumentError: No hash key: x
यह तकनीक डिबगिंग और रीफैक्टरिंग के लिए उपयोगी हो सकती है क्योंकि यह एक विशिष्ट हैश पर लागू होती है। यह इस व्यवहार को जोड़ने का एक बहुत कम दखल देने वाला तरीका है, जैसे कि हैश क्लास में बंदर-पैचिंग करना होगा।
नोट:मैं यह सुझाव नहीं दे रहा हूं कि कोई भी इसे नए कोड में हैश.फेच के स्थान पर इस्तेमाल करे। डिबगिंग और रीफैक्टरिंग के लिए अपनी आस्तीन ऊपर रखना सिर्फ एक दिलचस्प चाल है।
आलसी से जेनरेट की गई लुकअप टेबल
यह तकनीक गणना के परिणामों को कैशिंग करने के लिए उपयोगी है। कल्पना कीजिए कि आपको बहुत सारे वर्गमूलों की गणना करने की आवश्यकता है। आप नीचे दिए गए उदाहरण की तरह एक आलसी-आबादी वाली लुकअप तालिका बना सकते हैं।
sqrt_lookup = Hash.new { |hash, key| hash[key] = Math.sqrt(key) }
sqrt_lookup[9] # 3.0
sqrt_lookup[7] # 2.6457513110645907
sqrt_lookup # {9=>3.0, 7=>2.6457513110645907}
पुनरावर्ती आलसी लुकअप टेबल
मान लीजिए कि आपके पास एक पुनरावर्ती कार्य है और आप प्रत्येक रिकर्सन के परिणाम को कैश करना चाहते हैं। आइए एक उदाहरण के रूप में एक फैक्टोरियल गणना लें। "फोर फैक्टोरियल", उर्फ "4!" "4x3x2x1" कहने का एक और तरीका है। आप हैश का उपयोग करके इसे पुनरावर्ती रूप से कार्यान्वित कर सकते हैं। नीचे दिया गया उदाहरण, जो मैंने इस ब्लॉग पोस्ट से लिया है, इसे अच्छी तरह से प्रदर्शित करता है:
factorial = Hash.new do |h,k|
if k > 1
h[k] = h[k-1] * k
else
h[k] = 1
end
end
factorial[4] # 24
factorial # {1=>1, 2=>2, 3=>6, 4=>24}
आरंभीकरण के बाद डिफ़ॉल्ट को संशोधित करना
हैश बनने के बाद आप डिफ़ॉल्ट मान को भी नियंत्रित कर सकते हैं। ऐसा करने के लिए default
. का उपयोग करें और default_proc
बसता है।
h={}
h[:a] # nil
h.default = "new default"
h[:a] # "new default"
h.default_proc = Proc.new { Time.now.to_i }
h[:a] # 1435684014
रूबी ढूंढें:आलसी अनंत नेस्टेड हैश का खेल
केवल मनोरंजन के लिए, आइए इन सभी उपयोगी तकनीकों को एक अत्यंत बेकार उदाहरण में लपेट दें। वह पुराना टेक्स्ट-आधारित गेम एडवेंचर याद है? आइए इसका अब तक का सबसे बेवकूफी भरा संस्करण बनाएं।
कल्पना कीजिए कि आप एक गुफा में हैं। आप उत्तर, दक्षिण, पूर्व या पश्चिम जा सकते हैं। इनमें से तीन विकल्प आपको गुफा के एक नए "कमरे" में ले जाते हैं जहाँ आप खोज करते रहते हैं। लेकिन एक विकल्प आपको "रूबी" की ओर ले जाता है। इसलिए खेल का नाम:"माणिक खोजें।"
गुफा का प्रत्येक कमरा एक हैश से मेल खाता है। हैश में केवल एक प्रविष्टि है। यादृच्छिक रूप से चुने गए ["n", "s", "e", "w"] में से एक का मान "आपने माणिक पाया।" यदि आप गलत तरीके से चुनते हैं तो एक नया हैश बनाया जाता है और ट्री में जोड़ा जाता है।
generator = Proc.new do |hash, key|
hash[key] = Hash.new(&generator).merge(["n", "s", "e", "w"][rand(4)] => "You found the ruby!")
end
dungeon = Hash.new(&generator)
dungeon["n"] # <Hash ...
dungeon["n"]["s"] # <Hash ...
dungeon["n"]["s"]["w"] # "You found the ruby!"