यदि आप Ruby 3x3 प्रयास का अनुसरण कर रहे हैं, तो आपने शायद Optcarrot के बारे में सुना होगा। यह शुद्ध Ruby में लिखा गया एक NES एमुलेटर है।
मैं हाल ही में Optcarrot सोर्स को देख रहा था और एक दिलचस्प विवरण सामने आया। यह Ruby के हैश की एक विशेषता का व्यापक उपयोग करता है जिसे अक्सर अनदेखा किया जाता है, लेकिन काफी उपयोगी है। वह है किसी भी ऑब्जेक्ट को हैश कुंजी के रूप में उपयोग करने की क्षमता।
संदर्भ: NES मेमोरी मैपिंग
उच्च-स्तरीय प्रोग्रामर्स के रूप में, हम मेमोरी को RAM के रूप में सोचते हैं। लेकिन निम्न-स्तर पर, "मेमोरी" के कई अन्य उपयोग हैं।
"मेमोरी" को पढ़ना और लिखना है कि NES का CPU GPU, कंट्रोल पैड्स और कार्ट्रिज पर किसी विशेष इलेक्ट्रॉनिक्स के साथ कैसे संचार करता है। उपयोग किए गए पते के आधार पर, एक write_to_memory मेथड कॉल जॉयस्टिक को रीसेट कर सकता है, VRAM को स्वैप कर सकता है या ध्वनि बजा सकता है।
आप इसे Ruby में कैसे लागू करेंगे?
Optcarrot 65536 पतों में से प्रत्येक के लिए दो Method ऑब्जेक्ट्स स्टोर करके करता है। एक गेटर है और एक सेटर। यह ऐसा दिखता है:
@getter_methods[0x0001] = @ram.method(:[])
@setter_methods[0x0001] = @ram.method(:[]=)
समस्या: डुप्लिकेट ऑब्जेक्ट्स
इस तरह Object#method का उपयोग करने की समस्या यह है कि यह बहुत सारे व्यक्तिगत Method ऑब्जेक्ट्स बनाता है जो समान हैं।
हम इसे object_id को देखकर देख सकते हैं:
> a = []
> a.method(:[]=).object_id
=> 70142391223600
> a.method(:[]=).object_id
=> 70142391912420
दोनों Method ऑब्जेक्ट्स के अलग-अलग object_id मान हैं, इसलिए वे अलग-अलग ऑब्जेक्ट्स हैं भले ही वे एक ही काम करते हों।
आम तौर पर, हमें कुछ अतिरिक्त Method ऑब्जेक्ट्स की परवाह नहीं हो सकती, लेकिन इस मामले में हम हजारों के साथ काम कर रहे हैं।
समाधान: हैश के माध्यम से मेमोइज़िंग
Optcarrot डुप्लिकेट Method ऑब्जेक्ट समस्या से एक ऐसी ट्रिक के साथ बचता है जो इतनी सरल है कि इसे अनदेखा करना आसान है।
यह डुप्लीकेट्स को हटाने और मेमोइज़ करने के लिए एक हैश का उपयोग करता है। नीचे दिया गया सरलीकृत कोड इस तकनीक को प्रदर्शित करता है:
def initialize
@setter_methods = []
@setter_cache = {}
...
end
def add_setter(address, setter)
# डुप्लीकेट्स स्टोर न करें
@setter_cache[setter] ||= setter
// डिडुप्ड वर्जन का उपयोग करें
@setter_methods[address] = @setter_cache[setter]
end
यह काम करता है क्योंकि Hash को इस बात की परवाह नहीं है कि आप उसे कुंजी के रूप में किस प्रकार के ऑब्जेक्ट्स देते हैं।
यदि यह भ्रमित करने वाला है, तो इसे स्ट्रिंग्स के साथ IRB में आज़माएं:
> cache = {}
> cache["foo"] ||= "bar"
=> "bar"
cache["foo"] ||= "baz"
=> "bar"
अब, विचार करें कि Ruby में, स्ट्रिंग्स String क्लास के इंस्टेंस हैं। Ruby द्वारा स्ट्रिंग को हैश कुंजी के रूप में उपयोग करने के लिए उपयोग किया जाने वाला तंत्र मूल रूप से वही है जो Method ऑब्जेक्ट को स्टोर करने के लिए उपयोग किया जाता है।
Hash समानता की गणना कैसे करता है
गैर-स्ट्रिंग ऑब्जेक्ट्स को हैश कुंजी के रूप में उपयोग करते समय, प्रश्न उठता है: Hash कैसे जानता है कि दो ऑब्जेक्ट्स समान हैं?
उत्तर यह है कि यह Object#hash मेथड का उपयोग करता है। यह मेथड आपके ऑब्जेक्ट के माध्यम से जाता है और पुनरावर्ती रूप से एक हैश उत्पन्न करता है। यह ऐसा दिखता है:
> a.method(:[]=).hash
=> 929915641391564853
क्योंकि समान ऑब्जेक्ट्स समान हैश मान उत्पन्न करते हैं, इसका उपयोग समानता के परीक्षण के रूप में किया जा सकता है।
a.hash == b.hash
दिलचस्प रूप से पर्याप्त, यह वही दृष्टिकोण है जो eql? मेथड द्वारा उपयोग किया जाता है:
a.eql?(b)
यह हमारे उदाहरण में Method ऑब्जेक्ट्स के साथ काम करता है:
> a.method(:[]=).hash == a.method(:[]=).hash
=> true
निष्कर्ष
Ruby वेब डेवलपमेंट पैटर्न का आदी होने के बाद, मेरे लिए optcarrot सोर्स को देखना और देखना वास्तव में दिलचस्प था कि एक रियल-टाइम नॉन-वेब ऐप विभिन्न पैटर्न का उपयोग कैसे करता है। एक वेब ऐप में मुझे संदेह है कि मैं कभी भी 65536 तत्वों के साथ एक सरणी बनाऊंगा, लेकिन यहां, "डेस्कटॉप" ऐप के सेटअप के हिस्से के रूप में, यह बहुत मायने रखता है।
यदि आपके कोई प्रश्न या टिप्पणियां हैं, तो कृपया starr@honeybadger.io या ट्विटर पर @StarrHorne पर संपर्क करें।