संस्मरण एक ऐसी तकनीक है जिसका उपयोग आप अपने एक्सेसर विधियों को गति देने के लिए कर सकते हैं। यह उन तरीकों के परिणामों को कैश करता है जो समय लेने वाले काम करते हैं, ऐसे काम जिन्हें केवल एक बार करने की आवश्यकता होती है। रेल में, आप मेमोइज़ेशन को इतनी बार उपयोग करते हुए देखते हैं कि इसमें एक मॉड्यूल भी शामिल है जो आपके लिए विधियों को याद करेगा।
बाद में, यह विवादास्पद रूप से केवल वास्तव में सामान्य संस्मरण पैटर्न का उपयोग करने के पक्ष में हटा दिया गया था जिसके बारे में मैं पहले बात करूंगा। लेकिन जैसा कि आप देखेंगे, कुछ स्थान ऐसे हैं जहां यह मूल पैटर्न ठीक से काम नहीं करता है। इसलिए हम अधिक उन्नत संस्मरण पैटर्न को भी देखेंगे, और इस प्रक्रिया में रूबी के बारे में कुछ साफ-सुथरी बातें सीखेंगे!
सुपर बेसिक मेमोइज़ेशन
आप इस संस्मरण पैटर्न को रूबी में हर समय देखेंगे:
class User < ActiveRecord::Base
def twitter_followers
# assuming twitter_user.followers makes a network call
@twitter_followers ||= twitter_user.followers
end
end
||=
कमोबेश @twitter_followers = @twitter_followers || twitter_user.followers
. इसका मतलब है कि आप केवल पहली बार twitter_followers
. पर कॉल करने पर ही नेटवर्क कॉल करेंगे , और भविष्य की कॉल केवल आवृत्ति चर @twitter_followers
. का मान लौटाएगी ।
बहु-पंक्ति संस्मरण
कभी-कभी, धीमा कोड एक पंक्ति में बिना भयानक काम किए फिट नहीं होता। कोड की कई पंक्तियों के साथ काम करने के लिए मूल पैटर्न का विस्तार करने के कुछ तरीके हैं, लेकिन यह मेरा पसंदीदा है:
class User < ActiveRecord::Base
def main_address
@main_address ||= begin
maybe_main_address = home_address if prefers_home_address?
maybe_main_address = work_address unless maybe_main_address
maybe_main_address = addresses.first unless maybe_main_address
end
end
end
begin...end
रूबी में कोड का एक ब्लॉक बनाता है जिसे एक ही चीज़ के रूप में माना जा सकता है, जैसे {...}
सी-शैली की भाषाओं में। इसीलिए ||=
यहां ठीक वैसे ही काम करता है जैसे पहले किया करता था।
शून्य के बारे में क्या?
लेकिन इन संस्मरण पैटर्न में एक गंदी, छिपी हुई समस्या है। पहले उदाहरण में, क्या होगा यदि उपयोगकर्ता के पास ट्विटर खाता नहीं है, और ट्विटर अनुयायियों एपीआई ने nil
लौटाया है ? दूसरे में, क्या होगा यदि उपयोगकर्ता के पास कोई पता नहीं है, और ब्लॉक nil
returned लौटाता है ?
हर बार जब हम इस विधि को कॉल करेंगे, तो आवृत्ति चर nil
होगा , और हम महंगे फ़ेच फिर से करेंगे।
तो, ||=
शायद जाने का सही तरीका नहीं है। इसके बजाय, हमें nil
. के बीच अंतर करना होगा और undefined
:
class User < ActiveRecord::Base
def twitter_followers
return @twitter_followers if defined? @twitter_followers
@twitter_followers = twitter_user.followers
end
end
class User < ActiveRecord::Base
def main_address
return @main_address if defined? @main_address
@main_address = begin
main_address = home_address if prefers_home_address?
main_address ||= work_address
main_address ||= addresses.first # some semi-sensible default
end
end
end
दुर्भाग्य से, यह थोड़ा बदसूरत है, लेकिन यह nil
. के साथ काम करता है , false
, और सब कुछ। (nil
को संभालने के लिए मामले में, आप इस समस्या से बचने के लिए अशक्त वस्तुओं और खाली सरणियों का भी उपयोग कर सकते हैं। nil
avoid से बचने का एक और कारण !)
और पैरामीटर के बारे में क्या?
हमारे पास कुछ संस्मरण पैटर्न हैं जो साधारण एक्सेसर्स के लिए अच्छी तरह से काम करते हैं। लेकिन क्या होगा यदि आप एक ऐसी विधि को याद रखना चाहते हैं जो पैरामीटर लेती है, जैसे कि यह एक?
class City < ActiveRecord::Base
def self.top_cities(order_by)
where(top_city: true).order(order_by).to_a
end
end
यह पता चला है कि रूबी का Hash
एक इनिटैलाइज़र है जो पूरी तरह से काम करता है इस स्थिति के लिए। आप Hash.new
. पर कॉल कर सकते हैं एक ब्लॉक के साथ:
Hash.new {|h, key| h[key] = some_calculated_value }
फिर, हर बार जब आप हैश में एक कुंजी तक पहुंचने का प्रयास करते हैं जिसे सेट नहीं किया गया है, तो यह ब्लॉक को निष्पादित करेगा। और यह हैश को उस कुंजी के साथ पास कर देगा जिसे आपने ब्लॉक में एक्सेस करने का प्रयास किया था।
इसलिए, यदि आप इस विधि को याद रखना चाहते हैं, तो आप कुछ ऐसा कर सकते हैं:
class City < ActiveRecord::Base
def self.top_cities(order_by)
@top_cities ||= Hash.new do |h, key|
h[key] = where(top_city: true).order(key).to_a
end
@top_cities[order_by]
end
end
और इससे कोई फर्क नहीं पड़ता कि आप order_by
में क्या भेजते हैं , सही परिणाम याद हो जाएगा। चूंकि ब्लॉक केवल तभी कॉल किया जाता है जब कुंजी मौजूद नहीं होती है, इसलिए आपको ब्लॉक के शून्य या गलत होने के परिणाम के बारे में चिंता करने की आवश्यकता नहीं है।
आश्चर्यजनक रूप से, Hash
उन कुंजियों के साथ ठीक काम करता है जो वास्तव में सरणियाँ हैं:
h = {}
h[["a", "b"]] = "c"
h[["a", "b"]] # => "c"
तो आप इस पैटर्न का उपयोग किसी भी पैरामीटर के साथ विधियों में कर सकते हैं!
इस सारी परेशानी से क्यों गुजरना है?
बेशक, यदि आप इन संस्मरण पैटर्न को कई तरीकों से जोड़ना शुरू करते हैं, तो आपका कोड बहुत जल्दी अपठनीय हो जाएगा। आपके तरीके सभी समारोह होंगे और कोई सार नहीं।
इसलिए यदि आप किसी ऐसे ऐप पर काम कर रहे हैं जिसके लिए बहुत अधिक संस्मरण की आवश्यकता है, तो हो सकता है कि आप एक ऐसे रत्न का उपयोग करना चाहें जो आपके लिए एक अच्छे, मित्रवत API के साथ संस्मरण को संभालता है। मेमोइस्ट एक अच्छा लगता है, और रेल के समान ही सुंदर है। (या, अपने नए-नए संस्मरण ज्ञान के साथ, आप स्वयं भी इसे बनाने का प्रयास कर सकते हैं)।
लेकिन इस तरह के पैटर्न की जांच करना हमेशा दिलचस्प होता है, देखें कि उन्हें एक साथ कैसे रखा जाता है, वे कहां काम करते हैं, और तेज किनारे कहां हैं। और जब आप एक्सप्लोर करते हैं तो आप कुछ कम ज्ञात रूबी सुविधाओं के बारे में कुछ साफ-सुथरी चीजें सीख सकते हैं।