रूबी मैजिक के एक और एपिसोड का समय! इस बार, हम रूबी की सबसे जादुई विशेषताओं में से एक को देखेंगे, जो उन अधिकांश मेथड्स को प्रदान करती है जिनका आप रूबी की एन्युमरेबल क्लासेस जैसे Array, Hash और Range के साथ काम करते समय उपयोग करेंगे। इस प्रक्रिया में, हम सीखेंगे कि आप एन्युमरेबल ऑब्जेक्ट्स के साथ क्या कर सकते हैं, एन्युमरेशन कैसे काम करता है, और एक सिंगल मेथड को लागू करके ऑब्जेक्ट को एन्युमरेबल कैसे बनाया जाए।
Enumerable, #each और Enumerator
एन्युमरेशन ऑब्जेक्ट्स पर ट्रैवर्स करने को संदर्भित करता है। रूबी में, हम एक ऑब्जेक्ट को एन्युमरेबल कहते हैं जब यह आइटम्स के एक सेट और उनमें से प्रत्येक पर लूप करने के मेथड का वर्णन करता है।
बिल्ट-इन एन्युमरेबल्स को Enumerable मॉड्यूल को शामिल करके उनकी एन्युमरेशन सुविधाएँ मिलती हैं, जो #include?, #count, #map, #select और #uniq जैसे मेथड्स प्रदान करता है, दूसरों के अलावा। ऐरे और हैश से जुड़े अधिकांश मेथड्स वास्तव में इन क्लासेस में स्वयं लागू नहीं होते, वे शामिल होते हैं।
नोट: कुछ मेथड्स, जैसे Array क्लास पर #count और #take, हैं विशेष रूप से ऐरे के लिए लागू किए गए हैं बजाय Enumerable मॉड्यूल से उन का उपयोग करने के। यह आमतौर पर ऑपरेशन को तेज़ बनाने के लिए किया जाता है।
Enumerable मॉड्यूल #each नामक मेथड पर निर्भर करता है, जिसे किसी भी क्लास में लागू किया जाना चाहिए जिसमें यह शामिल है। जब एक ब्लॉक के साथ ऐरे पर कॉल किया जाता है, तो #each मेथड ऐरे के प्रत्येक एलिमेंट के लिए ब्लॉक को निष्पादित करेगा।
irb> [1,2,3].each { |i| puts "* #{i}" }
* 1
* 2
* 3
=> [1,2,3]
यदि हम ब्लॉक पास किए बिना ऐरे पर #each मेथड कॉल करते हैं, तो हमें Enumerator का एक इंस्टेंस प्राप्त होगा।
irb> [1,2,3].each
=> #<Enumerator: [1, 2, 3]:each>
Enumerator के इंस्टेंस वर्णन करते हैं कि किसी ऑब्जेक्ट पर कैसे इटरेट करना है। एन्युमरेटर्स ऑब्जेक्ट्स पर मैन्युअली इटरेट करते हैं और एन्युमरेशन चेन करते हैं।
irb> %w(dog cat mouse).each.with_index { |a, i| puts "#{a} is at position #{i}" }
dog is at position 0
cat is at position 1
mouse is at position 2
=> ["dog", "cat", "mouse"]
#with_index मेथड बदले हुए एन्युमरेटर्स कैसे काम करते हैं इसका एक अच्छा उदाहरण है। इस उदाहरण में, ऐरे पर #each कॉल करके एन्युमरेटर लौटाया जाता है। फिर, प्रत्येक ऐरे एलिमेंट में इंडेक्स जोड़ने के लिए #with_index कॉल किया जाता है ताकि प्रत्येक एलिमेंट का इंडेक्स प्रिंट किया जा सके।
ऑब्जेक्ट्स को एन्युमरेबल बनाना
हुड के नीचे, #max, #map और #take जैसे मेथड्स कार्य करने के लिए #each मेथड पर भरोसा करते हैं।
def max
max = nil
each do |item|
if !max || item > max
max = item
end
end
max
end
आंतरिक रूप से, Enumerable के मेथड्स के C कार्यान्वयन हैं, लेकिन उपरोक्त उदाहरण मोटे तौर पर दिखाता है कि #max कैसे काम करता है। #each का उपयोग करके सभी मानों पर लूप करके और उच्चतम को याद करके, यह अधिकतम मान लौटाता है।
def map(&block)
new_list = []
each do |item|
new_list << block.call(item)
end
new_list
end
#map फंक्शन पास किए गए ब्लॉक को प्रत्येक आइटम के साथ कॉल करता है और सभी मानों पर लूप करने के बाद लौटाने के लिए परिणाम को एक नई सूची में रखता है।
चूंकि Enumerable के सभी मेथड्स कुछ हद तक #each मेथड का उपयोग करते हैं, कस्टम क्लास को एन्युमरेबल बनाने में हमारा पहला कदम #each मेथड को लागू करना है।
#each लागू करना
#each फंक्शन को लागू करके और क्लास में Enumerable मॉड्यूल को शामिल करके, यह एन्युमरेबल बन जाता है और #min, #take और #inject जैसे मेथड्स मुफ्त में प्राप्त करता है।
हालांकि अधिकांश स्थितियां मौजूदा ऑब्जेक्ट जैसे ऐरे पर वापस जाने और उस पर #each मेथड कॉल करने की अनुमति देती हैं, आइए एक उदाहरण देखें जहां हमें इसे स्क्रैच से खुद लिखना होगा। इस उदाहरण में, हम #each को लिंक्ड लिस्ट पर लागू करेंगे ताकि यह एन्युमरेबल बन जाए।
लिंक्ड लिस्ट्स: ऐरे के बिना लिस्ट्स
लिंक्ड लिस्ट डेटा एलिमेंट्स का संग्रह है, जिसमें प्रत्येक एलिमेंट अगले की ओर इशारा करता है। लिस्ट में प्रत्येक एलिमेंट के दो मान होते हैं, जिन्हें हेड और टेल नाम दिया गया है। हेड एलिमेंट का मान रखता है, और टेल लिस्ट के बाकी हिस्से का लिंक है।
[42, [12, [73, nil]]
तीन मानों (42, 12 और 73) वाली लिंक्ड लिस्ट के लिए, पहले एलिमेंट का हेड 42 है, और टेल दूसरे एलिमेंट का लिंक है। दूसरे एलिमेंट का हेड 12 है, और टेल तीसरे एलिमेंट को रखता है। तीसरे एलिमेंट का हेड 73 है, और टेल nil है, जो लिस्ट के अंत को इंगित करता है।
रूबी में, लिंक्ड लिस्ट को @head और @tail नाम के दो इंस्टेंस वेरिएबल्स रखने वाली क्लास का उपयोग करके बनाया जा सकता है।
class LinkedList
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
end
#<< मेथड लिस्ट में नए मान जोड़ने के लिए उपयोग किया जाता है, जो पास किए गए मान को हेड और पिछली लिस्ट को टेल के रूप में एक नई लिस्ट लौटाकर काम करता है।
इस उदाहरण में, #inspect मेथड जोड़ा गया है ताकि हम लिस्ट में देख सकें कि इसमें कौन से एलिमेंट्स हैं।
irb> LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
अब जब हमारे पास लिंक्ड लिस्ट है, तो आइए इस पर #each लागू करें। #each फंक्शन एक ब्लॉक लेता है और ऑब्जेक्ट में प्रत्येक मान के लिए इसे निष्पादित करता है। इसे अपनी लिंक्ड लिस्ट पर लागू करते समय, हम लिस्ट की रिकर्सिव प्रकृति का लाभ उठा सकते हैं पास किए गए ब्लॉक को लिस्ट के @head पर कॉल करके, और @tail पर #each कॉल करके, यदि यह मौजूद है।
class LinkedList
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
block.call(@head)
@tail.each(&block) if @tail
end
end
हमारी लिंक्ड लिस्ट के इंस्टेंस पर #each कॉल करने पर, यह करंट @head के साथ पास किए गए ब्लॉक को कॉल करता है। फिर, यह @tail में लिंक्ड लिस्ट पर each को कॉल करता है जब तक टेल nil न हो।
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.each { |item| puts item }
42
12
73
=> nil
अब जब हमारी लिंक्ड लिस्ट #each का जवाब देती है, तो हम include Enumerable कर सकते हैं ताकि हमारी लिस्ट एन्युमरेबल बन जाए।
class LinkedList
include Enumerable
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
block.call(@head)
@tail.each(&block) if @tail
end
end
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.count
=> 3
irb> list.max
=> 73
irb> list.map { |item| item * item }
=> [1764, 144, 5329]
irb> list.select(&:even?)
=> [42, 12]
Enumerator इंस्टेंस लौटाना
अब हम अपनी लिंक्ड लिस्ट में सभी मानों पर लूप कर सकते हैं, लेकिन हम एन्युमरेबल फंक्शन्स को चेन नहीं कर सकते। ऐसा करने के लिए, जब हमारा #each फंक्शन ब्लॉक के बिना कहा जाता है तो हमें Enumerator इंस्टेंस लौटाने की आवश्यकता होगी।
class LinkedList
include Enumerable
def initialize(head, tail = nil)
@head, @tail = head, tail
end
def <<(item)
LinkedList.new(item, self)
end
def inspect
[@head, @tail].inspect
end
def each(&block)
if block_given?
block.call(@head)
@tail.each(&block) if @tail
else
to_enum(:each)
end
end
end
ऑब्जेक्ट को एन्युमरेटर में लपेटने के लिए, हम इस पर #to_enum मेथड कॉल करते हैं। हम :each पास करते हैं, क्योंकि वह मेथड है जिसका एन्युमरेटर को आंतरिक रूप से उपयोग करना चाहिए।
अब, हमारे #each मेथड को ब्लॉक के बिना कॉल करने से हमें एन्युमरेशन चेन करने की अनुमति मिलेगी।
irb> list = LinkedList.new(73) << 12 << 42
=> [42, [12, [73, nil]]]
irb> list.each
=> #<Enumerator: [42, [12, [73, nil]]]:each>
irb> list.map.with_index.to_h
=> {42=>0, 12=>1, 73=>2}
नौ लाइन्स कोड और एक include
Enumerable मॉड्यूल का उपयोग करके #each को लागू करके और अपने स्वयं के से Enumerator ऑब्जेक्ट्स लौटाकर, हम नौ लाइन्स कोड और एक include जोड़कर अपनी लिंक्ड लिस्ट को सुपरचार्ज करने में सक्षम थे।
यह रूबी में एन्युमरेबल्स का हमारा अवलोकन समाप्त करता है। हमें यह जानकर खुशी होगी कि आपने इस लेख के बारे में क्या सोचा, या यदि आपके कोई प्रश्न हैं। हम जांच और व्याख्या करने के लिए विषयों की तलाश में हमेशा रहते हैं, इसलिए यदि रूबी में कुछ जादुई है जिसे आप पढ़ना चाहेंगे, तो @AppSignal पर हमें बताने में संकोच न करें!