लैम्ब्डा का उपयोग कब करें, प्रॉन्यूव का उपयोग कब करें?


336

रूबी 1.8 में, एक तरफ proc / lambda और दूसरी तरफ के बीच सूक्ष्म अंतर हैं Proc.new

  • वे अंतर क्या हैं?
  • क्या आप इस बारे में दिशानिर्देश दे सकते हैं कि किसको चुनना है?
  • रूबी 1.9 में, proc और लैम्ब्डा अलग हैं। क्या बात है?

3
इसे भी देखें: माटज़ और फ़्लेगन द्वारा रूबी प्रोग्रामिंग लैंग्वेज बुक, इसने इस विषय को व्यापक रूप से कवर किया है। एक ब्लॉक उपज पैदावार की तरह व्यवहार करता है, जहां लंबोदर एक विधि की तरह व्यवहार करता है - विधि शब्दार्थ। इसके अलावा वापसी, ब्रेक, एट। सभी व्यवहारों में भिन्न होते हैं ncs lambdas
Gishu


आपने उस उत्तर को स्वीकार कर लिया है जो केवल कहता है कि खरीद और लैंबडा में क्या अंतर है, जबकि आपके प्रश्न का शीर्षक यह है कि उन चीजों का उपयोग कब करें
श्री

जवाबों:


378

एक और महत्वपूर्ण लेकिन सूक्ष्म अंतर के साथ निर्मित lambdaऔर procs के साथ बनाया गया Proc.newहै कि वे returnकथन को कैसे संभालते हैं :

  • एक lambdaगंभीर खरीद में, returnबयान केवल खरीद से वापस आ जाता है
  • एक Proc.newगंभीर खरीद में, returnबयान थोड़ा और अधिक आश्चर्यजनक है: यह न केवल खरीद से नियंत्रण लौटाता है, बल्कि खरीद को घेरने की विधि से भी!

यहां बताया गया है lambda-created proc के returnकार्रवाई में। यह इस तरह से व्यवहार करता है जैसा कि आप शायद उम्मीद करते हैं:

def whowouldwin

  mylambda = lambda {return "Freddy"}
  mylambda.call

  # mylambda gets called and returns "Freddy", and execution
  # continues on the next line

  return "Jason"

end


whowouldwin
#=> "Jason"

अब यहाँ एक है Proc.new-created proc के returnही बात कर। आप उन मामलों में से एक को देखने वाले हैं, जहां रूबी कम से कम कम से कम आश्चर्य के सिद्धांत को तोड़ती है:

def whowouldwin2

  myproc = Proc.new {return "Freddy"}
  myproc.call

  # myproc gets called and returns "Freddy", 
  # but also returns control from whowhouldwin2!
  # The line below *never* gets executed.

  return "Jason"

end


whowouldwin2         
#=> "Freddy"

यह आश्चर्य की बात व्यवहार (और साथ ही कम लिख कर) के लिए धन्यवाद, मैं का उपयोग कर के पक्ष में जाते हैं lambdaसे अधिक Proc.newजब procs बना रही है।


12
फिर procविधि भी है । क्या यह सिर्फ एक आशुलिपि है Proc.new?
panzi

6
@ अपंजी, हाँ, procके बराबर हैProc.new
ma11hew28

4
मेरी परीक्षणों में @mattdipasquale, procकार्य करता है की तरह lambdaऔर नहीं की तरह Proc.newवापसी बयान के संबंध में। इसका मतलब है कि माणिक डॉक गलत है।
केल्विन

31
@mattdipasquale क्षमा करें, मैं केवल आधा ही सही था। 1.8 की procतरह काम करता है lambda, लेकिन Proc.new1.9 की तरह काम करता है । पीटर वेजनेट का जवाब देखें।
केल्विन

55
यह "आश्चर्यजनक" व्यवहार क्यों है? एक lambdaअनाम विधि है। चूंकि यह एक विधि है, यह एक मान लौटाता है, और जिस विधि ने इसे बुलाया है वह इसके साथ जो चाहे कर सकता है, इसे अनदेखा करने और एक अलग मान वापस करने सहित। A Procकोड स्निपेट में चिपकाने जैसा है। यह एक विधि की तरह काम नहीं करता है। इसलिए जब वापसी होती है Proc, तो यह उस पद्धति के कोड का एक हिस्सा है जो इसे कहते हैं।
आर्कोलाई

96

आगे स्पष्टीकरण प्रदान करने के लिए:

जॉय का कहना है कि वापसी का व्यवहार Proc.new आश्चर्यजनक है। हालाँकि जब आप मानते हैं कि Proc.new एक ब्लॉक की तरह व्यवहार करता है तो यह आश्चर्य की बात नहीं है क्योंकि ठीक यही है कि ब्लॉक कैसे व्यवहार करते हैं। दूसरी ओर लांबा तरीकों की तरह अधिक व्यवहार करते हैं।

यह वास्तव में समझाता है कि प्रोक्स लचीले क्यों होते हैं जब यह arity (तर्कों की संख्या) की बात आती है जबकि लैम्ब्डा नहीं हैं। ब्लॉक को अपने सभी तर्कों को प्रदान करने की आवश्यकता नहीं है, लेकिन विधियां (जब तक कोई डिफ़ॉल्ट प्रदान नहीं की जाती है)। जबकि लैम्ब्डा डिफॉल्ट डिफ़ॉल्ट प्रदान करना रूबी 1.8 में एक विकल्प नहीं है, अब यह रूबी 1.9 में वैकल्पिक लैम्ब्डा सिंटैक्स (वेबमैट द्वारा नोट किया गया) के साथ समर्थित है:

concat = ->(a, b=2){ "#{a}#{b}" }
concat.call(4,5) # => "45"
concat.call(1)   # => "12"

और Michiel de Mare (ओपी) के बारे में गलत है Procs और लैम्ब्डा रूबी 1.9 में arity के साथ एक ही व्यवहार। मैंने सत्यापित किया है कि वे अभी भी ऊपर बताए अनुसार 1.8 से व्यवहार बनाए रखते हैं।

breakकथन वास्तव में या तो प्रॉक्स या लैम्ब्डा में बहुत ज्यादा मायने नहीं रखते हैं। Procs में, ब्रेक आपको Proc.new से लौटा देगा जो पहले ही पूरा हो चुका है। और इसका कोई मतलब नहीं है कि एक लंबोदर से तोड़ने के लिए क्योंकि यह अनिवार्य रूप से एक विधि है, और आप एक विधि के शीर्ष स्तर से कभी नहीं टूटेंगे।

next, redoऔर raiseप्रोक्स और लैम्ब्डा दोनों में समान व्यवहार करते हैं। जबकि retryदोनों में अनुमति नहीं है और कोई अपवाद नहीं उठाएगा।

और अंत में, procविधि का उपयोग कभी नहीं किया जाना चाहिए क्योंकि यह असंगत है और अप्रत्याशित व्यवहार है। रूबी 1.8 में यह वास्तव में एक लैम्ब्डा लौटाता है! रूबी 1.9 में यह तय किया गया है और यह एक प्रोक लौटाता है। यदि आप एक प्रोक बनाना चाहते हैं, तो साथ रहें Proc.new

अधिक जानकारी के लिए, मैं ओ'रेली की द रूबी प्रोग्रामिंग लैंग्वेज की अत्यधिक अनुशंसा करता हूं जो इस जानकारी के अधिकांश के लिए मेरा स्रोत है।


1
"" "हालांकि जब आप विचार करते हैं कि Proc.new एक ब्लॉक की तरह व्यवहार करता है तो यह आश्चर्य की बात नहीं है कि ठीक इसी तरह से ब्लॉक व्यवहार करता है।" "" <- ब्लॉक ऑब्जेक्ट का हिस्सा है, जबकि Proc.new एक ऑब्जेक्ट बनाता है। लैम्ब्डा और प्रोटॉन दोनों एक वस्तु बनाते हैं जिसका वर्ग प्रोक है, क्यों भिन्न है?
कमजोर

1
रूबी 2.5 के रूप में, breakप्रोक्स से उठता है LocalJumpError, जबकि breakलैम्ब्डा से बस return( जैसे , return nil) व्यवहार करता है ।
मसा सकानो

43

मैंने पाया यह पेज जो शो क्या बीच का अंतर Proc.newऔर lambdaकर रहे हैं। पृष्ठ के अनुसार, एकमात्र अंतर यह है कि एक लंबोदर उस तर्क की संख्या के बारे में सख्त है जिसे वह स्वीकार करता है, जबकि Proc.newलापता तर्क को परिवर्तित करता है nil। यहाँ एक उदाहरण IRB सत्र है जो अंतर दिखाता है:

irb (मुख्य): 001: 0> l = lambda {| x, y x + y}
=> # <प्रोक: 0x00007fc605ec0748 @ (irb): 1>
irb (मुख्य): 002: 0> p = Proc.new {| x, y | x + y}
=> # <प्रोक: 0x00007fc605ea8698 @ (irb): 2>
irb (मुख्य): 003: 0> l.call "हैलो", "दुनिया"
=> "हेलोवर्ल्ड"
irb (मुख्य): 004: 0> p.call "हैलो", "वर्ल्ड"
=> "हेलोवर्ल्ड"
irb (मुख्य): 005: 0> l.call "हैलो"
तर्क-वितर्क: तर्क की गलत संख्या (2 के लिए 1)
    (irb) से: 1
    (irb) से: 5: `कॉल 'में
    (irb) से: 5
    से: 0
irb (मुख्य): 006: 0> p.call "हैलो"
TypeError: nil को String में नहीं बदल सकते
    (irb) से: 2: `+ 'में
    (irb) से: 2
    (irb) से: 6: `कॉल 'में
    (irb) से: 6
    से: 0

जब तक आप विशेष रूप से त्रुटि सहिष्णु व्यवहार नहीं चाहते हैं, तब तक पेज लैम्ब्डा का उपयोग करने की भी सिफारिश करता है। मैं इस भावना से सहमत हूं। लैम्ब्डा का उपयोग करने से एक बालक अधिक संक्षिप्त लगता है, और इस तरह के महत्वहीन अंतर के साथ, यह औसत स्थिति में बेहतर विकल्प लगता है।

रूबी 1.9 के लिए के रूप में, माफ करना, मैं 1.9 में अभी तक नहीं देखा है, लेकिन मुझे नहीं लगता कि वे इसे इतना सब बदल देंगे (हालांकि इसके लिए अपना शब्द नहीं लेते हैं, ऐसा लगता है कि आपने कुछ बदलावों के बारे में सुना है, इसलिए मैं शायद वहां गलत हूं)।


2
procs भी lambdas की तुलना में अलग तरह से लौटते हैं।
कैम

"" "Proc.new अनुपलब्ध तर्कों को शून्य में रूपांतरित करता है" "" Proc.new अतिरिक्त तर्कों की भी अनदेखी करता है (बेशक लैंबडा यह एक त्रुटि के साथ शिकायत करता है)।
कमजोर

16

प्रोक अधिक पुराना है, लेकिन वापसी का शब्दार्थ मेरे लिए अत्यधिक उल्टा है (कम से कम जब मैं भाषा सीख रहा था) क्योंकि:

  1. यदि आप खरीद का उपयोग कर रहे हैं, तो आप कुछ प्रकार के कार्यात्मक प्रतिमान का उपयोग करने की संभावना रखते हैं।
  2. प्रोक एन्क्लोज़िंग स्कोप से बाहर लौट सकता है (पिछली प्रतिक्रियाओं को देखें), जो मूल रूप से एक गोटो है, और प्रकृति में अत्यधिक गैर-कार्यात्मक है।

लैम्ब्डा कार्यात्मक रूप से सुरक्षित है और इसके बारे में तर्क करना आसान है - मैं हमेशा खरीद के बजाय इसका उपयोग करता हूं।


11

मैं सूक्ष्म अंतर के बारे में ज्यादा नहीं कह सकता। हालांकि, मैं यह बता सकता हूं कि रूबी 1.9 अब लंबोदा और ब्लॉकों के लिए वैकल्पिक मापदंडों की अनुमति देता है।

यहाँ 1.9 के तहत छुरा लैंबडास के लिए नया वाक्यविन्यास है:

stabby = ->(msg='inside the stabby lambda') { puts msg }

रूबी 1.8 में वह वाक्य रचना नहीं थी। न तो ब्लॉक घोषित करने का पारंपरिक तरीका / लैंबडास वैकल्पिक वैकल्पिक समर्थन का समर्थन करता है:

# under 1.8
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }
SyntaxError: compile error
(irb):1: syntax error, unexpected '=', expecting tCOLON2 or '[' or '.'
l = lambda { |msg = 'inside the stabby lambda'|  puts msg }

रूबी 1.9, हालांकि, पुराने सिंटैक्स के साथ वैकल्पिक तर्कों का भी समर्थन करती है:

l = lambda { |msg = 'inside the regular lambda'|  puts msg }
#=> #<Proc:0x0e5dbc@(irb):1 (lambda)>
l.call
#=> inside the regular lambda
l.call('jeez')
#=> jeez

यदि आप तेंदुए या लिनक्स के लिए Ruby1.9 का निर्माण करना चाहते हैं, तो इस लेख को देखें (बेशर्म आत्म प्रचार)।


लैम्ब्डा के भीतर वैकल्पिक पैरामेट्स की बहुत जरूरत थी, मुझे खुशी है कि उन्होंने इसे 1.9 में जोड़ा है। मुझे लगता है कि ब्लॉक में वैकल्पिक पैरामीटर भी हो सकते हैं (1.9 में)?
mpd

आप ब्लॉकों में डिफ़ॉल्ट मापदंडों का प्रदर्शन नहीं कर रहे हैं, केवल लैम्ब्डा
iconoclast

11

संक्षिप्त उत्तर: क्या मायने रखता है return : लैम्ब्डा खुद से बाहर लौटता है, और खुद को और उस फ़ंक्शन को कॉल करता है जो इसे वापस करता है।

क्या कम स्पष्ट है आप प्रत्येक का उपयोग क्यों करना चाहते हैं। लैम्ब्डा वह है जो हम उम्मीद करते हैं कि चीजों को एक कार्यात्मक प्रोग्रामिंग अर्थ में करना चाहिए। यह मूल रूप से मौजूदा दायरे के साथ एक अनाम विधि है जो स्वचालित रूप से बाध्य है। दो में से, लैम्ब्डा वह है जिसका आपको संभवतः उपयोग करना चाहिए।

दूसरी ओर, प्रोक भाषा को लागू करने के लिए वास्तव में उपयोगी है। उदाहरण के लिए आप "यदि" कथन या "उनके साथ" छोरों के लिए लागू कर सकते हैं। प्रॉप में पाया गया कोई भी रिटर्न उस विधि से वापस आ जाएगा जिसने इसे कहा था, न कि केवल "यदि" कथन। यह है कि भाषाएं कैसे काम करती हैं, कैसे "यदि" कथन काम करते हैं, तो मेरा अनुमान है कि रूबी कवर के तहत इसका उपयोग करती है और उन्होंने इसे केवल इसलिए उजागर किया क्योंकि यह शक्तिशाली लग रहा था।

आपको वास्तव में इसकी आवश्यकता तभी होगी जब आप नए भाषा निर्माण जैसे लूप, इफ-कंस्ट्रक्शन आदि का निर्माण कर रहे हों।


1
"लैम्ब्डा खुद से बाहर लौटता है, और खुद से बाहर रिटर्न खरीदता है और यह कहा जाता है कि फ़ंक्शन" सादे गलत और एक बहुत ही सामान्य गलतफहमी है। एक प्रॉपर्टी एक क्लोजर है और इसे बनाने वाली विधि से रिटर्न मिलता है पृष्ठ पर कहीं और मेरा पूरा उत्तर देखें।
ComDubh

10

इसे देखने का एक अच्छा तरीका यह है कि लैम्ब्डा को अपने दायरे में निष्पादित किया जाता है (जैसे कि यह एक विधि कॉल था), जबकि प्रोक्स को कॉलिंग विधि के साथ इनलाइन निष्पादित के रूप में देखा जा सकता है, कम से कम यह एक अच्छा तरीका है जो विच का उपयोग करने का निर्णय लेता है प्रत्येक मामले में।


8

मैंने तीसरी विधि में किसी भी टिप्पणी पर ध्यान नहीं दिया, "proc" जो कि पदावनत है, लेकिन 1.8 और 1.9 में अलग तरीके से संभाला गया है।

यहां एक काफी वर्बोज़ उदाहरण दिया गया है जो तीन समान कॉल के बीच के अंतर को देखना आसान बनाता है:

def meth1
  puts "method start"

  pr = lambda { return }
  pr.call

  puts "method end"  
end

def meth2
  puts "method start"

  pr = Proc.new { return }
  pr.call

  puts "method end"  
end

def meth3
  puts "method start"

  pr = proc { return }
  pr.call

  puts "method end"  
end

puts "Using lambda"
meth1
puts "--------"
puts "using Proc.new"
meth2
puts "--------"
puts "using proc"
meth3

1
माटज़ ने कहा था कि उन्होंने इसे अपदस्थ करने की योजना बनाई क्योंकि यह खरीद करने के लिए भ्रामक था और Proc.new विभिन्न परिणाम लौटा रहा था। 1.9 में वे समान व्यवहार करते हैं (प्रो। प्रूवन के लिए एक उपनाम है)। eigenclass.org/hiki/Changes+in+Ruby+1.9#l47
डेव

@banister: proc1.8 में एक मेमना लौटा; अब 1.9 में एक खरीद वापस करने के लिए तय किया गया है - हालांकि यह एक परिवर्तन है; इसलिए अब उपयोग करने की अनुशंसा नहीं की गई
गिशु

मुझे लगता है कि पिकैक्स कहीं फुटनोट में कहता है कि खरीद प्रभावी रूप से वंचित है या कुछ और है। मेरे पास सटीक पृष्ठ संख्या नहीं है।
डर्टोनी

7

रूबी में क्लोजर रूबी के साथ ब्लॉक, लैम्ब्डा और प्रोक में काम करने के लिए एक अच्छा अवलोकन है।


मैंने इसे पढ़ने से रोक दिया जब मैंने पढ़ा "एक फ़ंक्शन कई ब्लॉकों को स्वीकार नहीं कर सकता है - उस सिद्धांत का उल्लंघन करना जो मूल्यों के रूप में स्वतंत्र रूप से चारों ओर पारित किया जा सकता है।" ब्लॉक बंद नहीं हो रहे हैं। प्रोक्स हैं, और एक फ़ंक्शन कई प्रॉक्स को स्वीकार कर सकता है।
ComDubh

5

लैम्ब्डा अन्य भाषाओं की तरह अपेक्षा के अनुरूप काम करता है।

तार Proc.newआश्चर्यचकित और भ्रमित है।

returnके द्वारा बनाई गई proc में बयान Proc.newकेवल अभी से ही नियंत्रण वापस नहीं होगा, लेकिन यह enclosing विधि से भी

def some_method
  myproc = Proc.new {return "End."}
  myproc.call

  # Any code below will not get executed!
  # ...
end

आप तर्क दे सकते हैं कि Proc.newकोड को ब्लॉक की तरह एन्क्लोजिंग विधि में सम्मिलित करता है। लेकिन Proc.newएक ऑब्जेक्ट बनाता है, जबकि ब्लॉक एक ऑब्जेक्ट का हिस्सा है

और लैम्ब्डा और के बीच एक और अंतर है Proc.new, जो उनके (गलत) तर्कों को संभालने का है। लैम्ब्डा इसके बारे में शिकायत करता है, जबकि Proc.newअतिरिक्त तर्कों की अनदेखी करता है या तर्कों के अभाव को शून्य मानता है।

irb(main):021:0> l = -> (x) { x.to_s }
=> #<Proc:0x8b63750@(irb):21 (lambda)>
irb(main):022:0> p = Proc.new { |x| x.to_s}
=> #<Proc:0x8b59494@(irb):22>
irb(main):025:0> l.call
ArgumentError: wrong number of arguments (0 for 1)
        from (irb):21:in `block in irb_binding'
        from (irb):25:in `call'
        from (irb):25
        from /usr/bin/irb:11:in `<main>'
irb(main):026:0> p.call
=> ""
irb(main):049:0> l.call 1, 2
ArgumentError: wrong number of arguments (2 for 1)
        from (irb):47:in `block in irb_binding'
        from (irb):49:in `call'
        from (irb):49
        from /usr/bin/irb:11:in `<main>'
irb(main):050:0> p.call 1, 2
=> "1"

बीटीडब्ल्यू, procरूबी 1.8 में लैम्बडा बनाता है, जबकि रूबी में 1.9+ ऐसा व्यवहार करता है Proc.new, जो वास्तव में भ्रामक है।


3

एकॉर्डियन गाय की प्रतिक्रिया के बारे में विस्तार से बताने के लिए:

नोटिस जो Proc.newएक ब्लॉक को पारित करके एक खरीद बनाता है। मेरा मानना ​​है कि lambda {...}एक विधि कॉल के बजाय, जो एक ब्लॉक से गुजरता है, एक प्रकार के शाब्दिक के रूप में पार्स किया जाता है। returnएक विधि कॉल से जुड़े ब्लॉक के अंदर से विधि से वापस आ जाएगी, न कि ब्लॉक से, और इस Proc.newमामले में खेलने का एक उदाहरण है।

(यह 1.8 है। मुझे नहीं पता कि यह 1.9 में कैसे अनुवाद करता है।)


3

मुझे इस पर थोड़ी देर हो गई है, लेकिन Proc.newटिप्पणियों में उल्लेख नहीं किए जाने के बारे में एक महान लेकिन छोटी ज्ञात चीज है । द्वारा के रूप में प्रलेखन :

Proc::newकेवल एक ब्लॉक के साथ एक ब्लॉक के बिना एक संलग्न ब्लॉक के साथ एक विधि के बिना बुलाया जा सकता है, उस स्थिति में उस ब्लॉक कोProc ऑब्जेक्ट में बदल दिया जाता है।

कहा कि, Proc.newतरीकों की पैदावार को चेन करने देता है:

def m1
  yield 'Finally!' if block_given?
end

def m2
  m1 &Proc.new
end

m2 { |e| puts e } 
#⇒ Finally!

दिलचस्प है, यह वही काम करता है जिसमें एक &blockतर्क की घोषणा की जाती है def, लेकिन बिना डीआरजी सूची में ऐसा करने के लिए।
jrochkind

2

यह जोर देने के लायक है कि returnएक खरीद में lexically संलग्न विधि से लौटा है, अर्थात जिस विधि से खरीदारी की गई थी , वह विधि नहीं है जिसे खरीद कहा जाता है। यह प्रॉक्स की क्लोजर प्रॉपर्टी का नतीजा है। तो निम्न कोड कुछ भी नहीं आउटपुट:

def foo
  proc = Proc.new{return}
  foobar(proc)
  puts 'foo'
end

def foobar(proc)
  proc.call
  puts 'foobar'
end

foo

हालांकि proc में कार्यान्वित foobar, उस में बनाया गया था fooऔर इसलिए returnबाहर निकलता है foo, न सिर्फ foobar। जैसा कि चार्ल्स कैल्डवेल ने ऊपर लिखा है, यह एक GOTO महसूस करता है। मेरी राय में, returnएक ऐसे ब्लॉक में ठीक है जिसे इसके शाब्दिक संदर्भ में निष्पादित किया जाता है, लेकिन एक अलग संदर्भ में निष्पादित होने वाले किसी प्रोक में उपयोग किए जाने पर बहुत कम सहज है।


1

returnIMHO के साथ व्यवहार में अंतर 2 के बीच सबसे महत्वपूर्ण अंतर है। मैं लैंबडा को भी पसंद करता हूं क्योंकि यह प्रोटॉन्यू की तुलना में कम टाइपिंग है :-)


2
अपडेट करने के लिए: अब प्रोक्स का उपयोग करके बनाया जा सकता है proc {}। मुझे यकीन नहीं है कि यह कब लागू हुआ, लेकिन यह आदि को टाइप करने की तुलना में थोड़ा (आसान) है।
aceofbassgreg
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.