रूबी ब्लॉक में 'रिटर्न' का उपयोग करना


87

मैं एम्बेडेड स्क्रिप्टिंग भाषा के लिए रूबी 1.9.1 का उपयोग करने की कोशिश कर रहा हूं, ताकि "एंड-यूज़र" कोड रूबी ब्लॉक में लिखा जाए। इसके साथ एक मुद्दा यह है कि मैं चाहूंगा कि उपयोगकर्ता ब्लॉक में 'वापसी' कीवर्ड का उपयोग करने में सक्षम हों, इसलिए उन्हें निहित रिटर्न मानों के बारे में चिंता करने की आवश्यकता नहीं है। इसे ध्यान में रखते हुए, मैं इस तरह की चीज करना चाहता हूं:

def thing(*args, &block)
  value = block.call
  puts "value=#{value}"
end

thing {
  return 6 * 7
}

यदि मैं उपरोक्त उदाहरण में 'रिटर्न' का उपयोग करता हूं, तो मुझे एक लोकलजंपायर मिलता है। मुझे पता है कि ऐसा इसलिए है क्योंकि विचाराधीन ब्लॉक एक प्रोक है न कि लैम्ब्डा। यदि मैं 'रिटर्न' को हटाता हूं तो कोड काम करता है, लेकिन मैं वास्तव में इस परिदृश्य में 'रिटर्न' का उपयोग करने में सक्षम होना चाहूंगा। क्या यह संभव है? मैंने खंड को लंबोदर में बदलने की कोशिश की है, लेकिन परिणाम समान है।


आप एक अंतर्निहित वापसी मूल्य से क्यों बचना चाहते हैं?
marcgg

@marcgg - मेरा यहाँ एक संबंधित प्रश्न है - stackoverflow.com/questions/25953519/…
सिड स्मिथ

जवाबों:


171

बस nextइस संदर्भ में उपयोग करें:

$ irb
irb(main):001:0> def thing(*args, &block)
irb(main):002:1>   value = block.call
irb(main):003:1>   puts "value=#{value}"
irb(main):004:1> end
=> nil
irb(main):005:0>
irb(main):006:0* thing {
irb(main):007:1*   return 6 * 7
irb(main):008:1> }
LocalJumpError: unexpected return
        from (irb):7:in `block in irb_binding'
        from (irb):2:in `call'
        from (irb):2:in `thing'
        from (irb):6
        from /home/mirko/.rvm/rubies/ruby-1.9.1-p378/bin/irb:15:in `<main>'
irb(main):009:0> thing { break 6 * 7 }
=> 42
irb(main):011:0> thing { next 6 * 7 }
value=42
=> nil
  • return हमेशा विधि से लौटता है, लेकिन यदि आप इस स्निपेट का परीक्षण irb में करते हैं तो आपके पास विधि नहीं है, इसीलिए आपके पास है LocalJumpError
  • breakब्लॉक से मान लौटाता है और इसकी कॉल समाप्त करता है। यदि आपका ब्लॉक द्वारा बुलाया गया था yieldया .callहै, तो breakइस पुनरावर्तक से टूट जाता है भी
  • nextब्लॉक से मान लौटाता है और इसकी कॉल समाप्त करता है। यदि आपका ब्लॉक द्वारा बुलाया गया था yieldया .callहै, तो nextजहां लाइन के लिए मान देता है yieldबुलाया गया था

4
एक ब्रेक में एक अपवाद को बढ़ा देगा
gfreezy

क्या आप इस बात का हवाला दे सकते हैं कि आपको यह जानकारी "ब्लॉक से अगला रिटर्न मूल्य और कॉल समाप्त होता है" से मिलती है। मैं इस पर और पढ़ना चाहता हूं।
user566245

यह से था रूबी प्रोग्रामिंग भाषा पुस्तक (मैं इसे हाथ में नहीं है अभी) अगर मैं सही ढंग से याद है। मैं तो बस गूगल की जाँच की और मेरा मानना है कि यह है कि पुस्तक से है: librairie.immateriel.fr/fr/read_book/9780596516178/... और वहाँ से 2 अगले पृष्ठ X (यह मेरी सामग्री और अपने पृष्ठों नहीं है, मैं सिर्फ यह googled)। लेकिन मैं वास्तव में मूल पुस्तक की सिफारिश करता हूं, इसमें बहुत अधिक रत्न समझाया गया है।
एमबीओ

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

काश यह जवाब सबसे ऊपर होता। मैं इसे पर्याप्त रूप से उभार नहीं सकता।
btx9000

20

आप रूबी में ऐसा नहीं कर सकते।

returnकीवर्ड हमेशा विधि या वर्तमान संदर्भ में लैम्ब्डा से वापस आती है। ब्लॉकों में, यह उस विधि से वापस आ जाएगा जिसमें बंद को परिभाषित किया गया था । इसे कॉलिंग विधि या लैम्ब्डा से वापस करने के लिए नहीं बनाया जा सकता है ।

Rubyspec यह दर्शाता है कि यह वास्तव में रूबी के लिए सही व्यवहार (वैसे नहीं एक वास्तविक कार्यान्वयन, लेकिन उद्देश्य सी रूबी के साथ पूर्ण संगतता) है:

describe "The return keyword" do
# ...
describe "within a block" do
# ...
it "causes the method that lexically encloses the block to return" do
# ...
it "returns from the lexically enclosing method even in case of chained calls" do
# ...

यहाँ एक ब्लॉक /
प्रॉप

3

आप इसे गलत दृष्टिकोण से देख रहे हैं। यह एक मुद्दा है thing, मेमने का नहीं।

def thing(*args, &block)
  block.call.tap do |value|
    puts "value=#{value}"
  end
end

thing {
  6 * 7
}

1

बात कहाँ है? क्या आप एक कक्षा के अंदर हैं?

आप इस तरह से कुछ का उपयोग करने पर विचार कर सकते हैं:

class MyThing
  def ret b
    @retval = b
  end

  def thing(*args, &block)
    implicit = block.call
    value = @retval || implicit
    puts "value=#{value}"
  end

  def example1
    thing do
      ret 5 * 6
      4
    end
  end

  def example2
    thing do
      5 * 6
    end
  end
end

1

मैं एक ही मुद्दा था रूबी में एक वेब फ्रेमवर्क के लिए एक डीएसएल लिखना ... (वेब ​​फ्रेमवर्क एनोरेक्सिक रॉक होगा!) ...!

वैसे भी, मैंने रूबी इंटर्नल में खोदा और लोकलजंपायर का उपयोग करके एक सरल समाधान पाया जब एक प्रोक कॉल लौटाता है ... यह अब तक के परीक्षणों में अच्छी तरह से चलता है, लेकिन मुझे यकीन नहीं है कि यह पूर्ण-प्रमाण है:

def thing(*args, &block)
  if block
    block_response = nil
    begin
      block_response = block.call
    rescue Exception => e
       if e.message == "unexpected return"
          block_response = e.exit_value
       else
          raise e 
       end
    end
    puts "value=#{block_response}"
  else
    puts "no block given"
  end
end

अगर बचाव खंड में बयान कुछ इस तरह लग सकता है:

if e.is_a? LocalJumpError

लेकिन यह मेरे लिए अपरिवर्तित क्षेत्र है, इसलिए मैंने अब तक जो भी परीक्षण किया है, उससे चिपके रहूंगा।


1

मेरा मानना ​​है कि कमियों के बावजूद यह सही उत्तर है:

def return_wrap(&block)
  Thread.new { return yield }.join
rescue LocalJumpError => ex
  ex.exit_value
end

def thing(*args, &block)
  value = return_wrap(&block)
  puts "value=#{value}"
end

thing {
  return 6 * 7
}

यह हैक उपयोगकर्ताओं को बिना परिणाम, स्व संरक्षित, आदि के बिना अपने गद्य में वापसी का उपयोग करने की अनुमति देता है।

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

मुख्य नुकसान संभावित ओवरहेड है (आप थ्रेड को प्रतिस्थापित कर सकते हैं + बस के साथ जुड़ें yieldयदि आपके परिदृश्य में पर्याप्त है)।


1

मुझे एक रास्ता मिला, लेकिन इसमें एक विधि को मध्यवर्ती चरण के रूप में परिभाषित करना शामिल है:

def thing(*args, &block)
  define_method(:__thing, &block)
  puts "value=#{__thing}"
end

thing { return 6 * 7 }
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.