रूबी मैजिक में हम उन चीजों के पीछे के जादू में गोता लगाना पसंद करते हैं जिनका उपयोग हम यह समझने के लिए करते हैं कि वे कैसे काम करते हैं। इस संस्करण में, हम ब्लॉक, प्रोसेस और लैम्ब्डा के बीच के अंतरों का पता लगाएंगे।
प्रथम श्रेणी के कार्यों के साथ प्रोग्रामिंग भाषाओं में, कार्यों को चर में संग्रहीत किया जा सकता है और अन्य कार्यों के लिए तर्क के रूप में पारित किया जा सकता है। फ़ंक्शंस अन्य फ़ंक्शंस का उपयोग उनके रिटर्न वैल्यू के रूप में भी कर सकते हैं।
एक बंद एक पर्यावरण के साथ एक प्रथम श्रेणी का कार्य है। पर्यावरण उन चरों का मानचित्रण है जो बंद होने के समय मौजूद थे। क्लोजर इन वेरिएबल तक अपनी पहुंच बनाए रखेगा, भले ही वे किसी अन्य दायरे में परिभाषित हों।
रूबी में प्रथम श्रेणी के कार्य नहीं हैं, लेकिन इसमें ब्लॉक, प्रोसेस और लैम्ब्डा के रूप में क्लोजर हैं। ब्लॉक का उपयोग कोड के ब्लॉक को विधियों में पास करने के लिए किया जाता है, और प्रोसेस और लैम्ब्डा वेरिएबल में कोड के ब्लॉक को स्टोर करने की अनुमति देते हैं।
ब्लॉक
रूबी में, ब्लॉक कोड के स्निपेट हैं जिन्हें बाद में निष्पादित करने के लिए बनाया जा सकता है। ब्लॉक उन तरीकों को पास कर दिए जाते हैं जो उन्हें do
. के भीतर उत्पन्न करते हैं और end
खोजशब्द। कई उदाहरणों में से एक है #each
विधि, जो गणना योग्य वस्तुओं पर लूप करती है।
[1,2,3].each do |n|
puts "#{n}!"
end
[1,2,3].each { |n| puts "#{n}!" } # the one-line equivalent.
इस उदाहरण में, Array#each
. को एक ब्लॉक पास किया जाता है विधि, जो सरणी में प्रत्येक आइटम के लिए ब्लॉक चलाती है और इसे कंसोल पर प्रिंट करती है।
def each
i = 0
while i < size
yield at(i)
i += 1
end
end
Array#each
. के इस सरलीकृत उदाहरण में , while
. में लूप, yield
सरणी में प्रत्येक आइटम के लिए पारित ब्लॉक को निष्पादित करने के लिए कहा जाता है। ध्यान दें कि इस पद्धति में कोई तर्क नहीं है, क्योंकि ब्लॉक को विधि में निहित रूप से पारित किया गया है।
अंतर्निहित ब्लॉक और yield
कीवर्ड
रूबी में, विधियां स्पष्ट रूप से और स्पष्ट रूप से ब्लॉक ले सकती हैं। yield
. को कॉल करके इंप्लिकेट ब्लॉक पासिंग कार्य एक विधि में कीवर्ड। yield
कीवर्ड विशेष है। यह एक पारित ब्लॉक को ढूंढता है और कॉल करता है, इसलिए आपको ब्लॉक को उन तर्कों की सूची में जोड़ने की ज़रूरत नहीं है जो विधि स्वीकार करती है।
चूंकि रूबी अंतर्निहित ब्लॉक पासिंग की अनुमति देता है, आप ब्लॉक के साथ सभी विधियों को कॉल कर सकते हैं। अगर यह कॉल नहीं करता है yield
, ब्लॉक को अनदेखा कर दिया जाता है।
irb> "foo bar baz".split { p "block!" }
=> ["foo", "bar", "baz"]
अगर कॉल किया गया तरीका करता है उपज, पारित ब्लॉक पाया जाता है और किसी भी तर्क के साथ बुलाया जाता है जो yield
. को पारित किया गया था कीवर्ड।
def each
return to_enum(:each) unless block_given?
i = 0
while i < size
yield at(i)
i += 1
end
end
यह उदाहरण Enumerator
. का एक उदाहरण देता है जब तक कोई ब्लॉक नहीं दिया जाता है।
yield
और block_given?
कीवर्ड वर्तमान दायरे में ब्लॉक ढूंढते हैं। यह ब्लॉक को परोक्ष रूप से पास करने की अनुमति देता है, लेकिन कोड को सीधे ब्लॉक तक पहुंचने से रोकता है क्योंकि यह एक चर में संग्रहीत नहीं है।
स्पष्ट रूप से पासिंग ब्लॉक
हम किसी विधि में किसी ब्लॉक को एम्परसेंड पैरामीटर (जिसे आमतौर पर &block
कहते हैं) का उपयोग करके तर्क के रूप में जोड़कर स्पष्ट रूप से स्वीकार कर सकते हैं ) चूंकि ब्लॉक अब स्पष्ट है, हम #call
. का उपयोग कर सकते हैं yield
. पर निर्भर होने के बजाय सीधे परिणामी वस्तु पर विधि ।
&block
तर्क उचित तर्क नहीं है, इसलिए इस विधि को किसी ब्लॉक के अलावा किसी अन्य चीज़ के साथ कॉल करने से ArgumentError
उत्पन्न होगा ।
def each_explicit(&block)
return to_enum(:each) unless block
i = 0
while i < size
block.call at(i)
i += 1
end
end
जब एक ब्लॉक को इस तरह से पास किया जाता है और एक चर में संग्रहीत किया जाता है, तो यह स्वचालित रूप से एक proc . में परिवर्तित हो जाता है ।
प्रक्रिया
एक "proc" Proc
. का एक उदाहरण है वर्ग, जिसमें निष्पादित करने के लिए एक कोड ब्लॉक होता है, और एक चर में संग्रहीत किया जा सकता है। एक खरीद बनाने के लिए, आप Proc.new
. पर कॉल करें और इसे एक ब्लॉक पास करें।
proc = Proc.new { |n| puts "#{n}!" }
चूंकि एक खरीद को एक चर में संग्रहीत किया जा सकता है, इसे सामान्य तर्क की तरह ही एक विधि में भी पारित किया जा सकता है। उस स्थिति में, हम एम्परसेंड का उपयोग नहीं करते हैं, क्योंकि खरीद स्पष्ट रूप से पारित हो जाती है।
def run_proc_with_random_number(proc)
proc.call(random)
end
proc = Proc.new { |n| puts "#{n}!" }
run_proc_with_random_number(proc)
एक खरीद बनाने और उस विधि को पास करने के बजाय, आप रूबी के एम्परसेंड पैरामीटर सिंटैक्स का उपयोग कर सकते हैं जिसे हमने पहले देखा था और इसके बजाय एक ब्लॉक का उपयोग कर सकते हैं।
def run_proc_with_random_number(&proc)
proc.call(random)
end
run_proc_with_random_number { |n| puts "#{n}!" }
विधि में तर्क में जोड़े गए एम्परसेंड को नोट करें। यह एक पारित ब्लॉक को एक खरीद वस्तु में बदल देगा और इसे विधि के दायरे में एक चर में संग्रहीत करेगा।
टिप :हालांकि कुछ स्थितियों में विधि में proc का होना उपयोगी होता है, लेकिन किसी ब्लॉक का proc में रूपांतरण एक प्रदर्शन हिट उत्पन्न करता है। जब भी संभव हो, इसके बजाय निहित ब्लॉकों का उपयोग करें।
#to_proc
प्रतीकों, हैश और विधियों को उनके #to_proc
. का उपयोग करके प्रोसेस में बदला जा सकता है तरीके। इसका अक्सर देखा जाने वाला उपयोग एक प्रतीक से एक विधि के लिए बनाई गई खरीद को पारित कर रहा है।
[1,2,3].map(&:to_s)
[1,2,3].map {|i| i.to_s }
[1,2,3].map {|i| i.send(:to_s) }
यह उदाहरण #to_s
. को कॉल करने के तीन समान तरीकों को दिखाता है सरणी के प्रत्येक तत्व पर। पहले एक में, एक एम्परसेंड के साथ एक प्रतीक पारित किया जाता है, जो अपने #to_proc
को कॉल करके स्वचालित रूप से इसे एक खरीद में परिवर्तित कर देता है। तरीका। अंतिम दो दिखाते हैं कि वह खरीद कैसी दिख सकती है।
class Symbol
def to_proc
Proc.new { |i| i.send(self) }
end
end
हालांकि यह एक सरलीकृत उदाहरण है, Symbol#to_proc
. का कार्यान्वयन दिखाता है कि हुड के नीचे क्या हो रहा है। विधि एक खरीद लौटाती है जो एक तर्क लेती है और self
. भेजती है इसके लिए। चूंकि self
इस संदर्भ में प्रतीक है, इसे Integer#to_s
. कहते हैं विधि।
लैम्बडास
लैम्ब्डा अनिवार्य रूप से कुछ विशिष्ट कारकों के साथ प्रोसेस होते हैं। वे दो तरह से "नियमित" विधियों की तरह हैं:वे कॉल किए जाने पर पारित तर्कों की संख्या को लागू करते हैं और वे "सामान्य" रिटर्न का उपयोग करते हैं।
एक लैम्ब्डा को कॉल करते समय जो बिना किसी तर्क की अपेक्षा करता है, या यदि आप लैम्ब्डा को तर्क देते हैं जो इसकी अपेक्षा नहीं करता है, तो रूबी एक ArgumentError
उठाती है ।
irb> lambda (a) { a }.call
ArgumentError: wrong number of arguments (given 0, expected 1)
from (irb):8:in `block in irb_binding'
from (irb):8
from /Users/jeff/.asdf/installs/ruby/2.3.0/bin/irb:11:in `<main>'
साथ ही, लैम्ब्डा रिटर्न कीवर्ड को उसी तरह से ट्रीट करता है जैसे कोई मेथड करता है। प्रो को कॉल करते समय, प्रोग्राम प्रो में कोड ब्लॉक पर नियंत्रण देता है। इसलिए, यदि खरीद वापस आती है, तो वर्तमान दायरा वापस आ जाता है। यदि किसी फ़ंक्शन के अंदर एक खरीद को कॉल किया जाता है और return
calls को कॉल करता है , फ़ंक्शन तुरंत भी वापस आ जाता है।
def return_from_proc
a = Proc.new { return 10 }.call
puts "This will never be printed."
end
यह फ़ंक्शन खरीद को नियंत्रण देगा, इसलिए जब यह वापस आता है, तो फ़ंक्शन वापस आ जाता है। इस उदाहरण में फ़ंक्शन को कॉल करने से कभी भी आउटपुट प्रिंट नहीं होगा और 10 वापस नहीं आएगा।
def return_from_lambda
a = lambda { return 10 }.call
puts "The lambda returned #{a}, and this will be printed."
end
लैम्ब्डा का उपयोग करते समय, यह करेगा मुद्रित हो। कॉल करना return
लैम्ब्डा में return
को कॉल करने जैसा व्यवहार करेगा एक विधि में, इसलिए a
वेरिएबल 10
. से भरा हुआ है और लाइन कंसोल पर प्रिंट हो जाती है।
ब्लॉक, प्रोसेस और लैम्ब्डा
अब जबकि हम दोनों ब्लॉक, प्रोसेस और लैम्ब्डा में जा चुके हैं, आइए वापस ज़ूम आउट करें और तुलना को सारांशित करें।
- रूबी में कोड के बिट्स को फंक्शन में पास करने के लिए ब्लॉक का व्यापक रूप से उपयोग किया जाता है।
yield
. का उपयोग करके कीवर्ड, एक ब्लॉक को एक खरीद में परिवर्तित किए बिना निहित रूप से पारित किया जा सकता है। - एम्पर्सेंड के साथ पहले पैरामीटर का उपयोग करते समय, एक विधि के लिए एक ब्लॉक पास करने से विधि के संदर्भ में एक खरीद होती है। प्रोसेस ब्लॉक की तरह व्यवहार करते हैं, लेकिन उन्हें एक वेरिएबल में स्टोर किया जा सकता है।
- लैम्ब्डा ऐसे प्रोसेस हैं जो तरीकों की तरह व्यवहार करते हैं, जिसका अर्थ है कि वे अपने मूल दायरे के बजाय तरीकों के रूप में उदारता और वापसी को लागू करते हैं।
यह रूबी में बंद होने पर हमारे विचार को समाप्त करता है। लेक्सिकल स्कोप और बाइंडिंग जैसे क्लोजर के बारे में जानने के लिए और भी बहुत कुछ है, लेकिन हम इसे भविष्य के एपिसोड के लिए रखेंगे। इस बीच, कृपया हमें बताएं कि आप रूबी मैजिक की भविष्य की किस्त के बारे में क्या पढ़ना चाहते हैं, बंद या अन्यथा @AppSignal पर।