रूबी में "प्रत्येक बनाम"


200

मुझे बस रूबी में छोरों के बारे में एक त्वरित सवाल था। क्या संग्रह के माध्यम से पुनरावृत्ति के इन दो तरीकों के बीच अंतर है?

# way 1
@collection.each do |item|
  # do whatever
end

# way 2
for item in @collection
  # do whatever
end

बस सोच रहा था कि क्या ये बिल्कुल समान हैं या हो सकता है कि शायद एक सूक्ष्म अंतर है (संभवतः जब @collectionशून्य है)।

जवाबों:


315

यह एकमात्र अंतर है:

से प्रत्येक:

irb> [1,2,3].each { |x| }
  => [1, 2, 3]
irb> x
NameError: undefined local variable or method `x' for main:Object
    from (irb):2
    from :0

के लिये:

irb> for x in [1,2,3]; end
  => [1, 2, 3]
irb> x
  => 3

forलूप के साथ , इटरेटर चर ब्लॉक होने के बाद भी रहता है। eachलूप के साथ , यह नहीं होता है, जब तक कि लूप शुरू होने से पहले ही इसे स्थानीय चर के रूप में परिभाषित नहीं किया गया था।

इसके अलावा, विधि के forलिए सिर्फ सिंटेक्स चीनी है each

जब @collectionहै nilदोनों छोरों एक अपवाद फेंक:

अपवाद: अपरिभाषित स्थानीय चर या विधि `@collection 'मुख्य के लिए: वस्तु


3
क्या कोई अच्छा कारण है कि x केस के लिए बना रहता है या यह खराब डिज़ाइन है: P? मुझे लगता है कि यह अन्य भाषाओं के साथ तुलना में अकल्पनीय है।
cc115

3
@ cyc115 कारण है कि xअवशेष में के लिए परिदृश्य तथ्य यह है कि (आम तौर पर) कीवर्ड नए कार्यक्षेत्रों का निर्माण नहीं करतीं के कारण है। यदि , जब तक , शुरू , के लिए , जबकि , आदि सभी वर्तमान दायरे के साथ काम करते हैं। #eachहालांकि एक ब्लॉक स्वीकार करता है। ब्लॉक हमेशा मौजूदा दायरे के शीर्ष पर अपना स्वयं का दायरा जोड़ते हैं। मतलब यह है कि ब्लॉक में एक नया वैरिएबल घोषित करना (इस तरह एक नया स्कोप) ब्लॉक के बाहर से सुलभ नहीं होगा क्योंकि वहां अतिरिक्त स्कोप उपलब्ध नहीं है।
3limin4t0r

43

एक अच्छी व्याख्या के लिए " द एविल्स ऑफ द लूप " देखें (वैरिएबल स्कूपिंग पर विचार करने वाला एक छोटा अंतर है)।

का उपयोग करना eachहै और अधिक मुहावरेदार माना रूबी का उपयोग।


@zachlatta: सूचित करने के लिए धन्यवाद। मैं लेख के एक webarchive.org संस्करण की ओर इशारा करने के लिए लिंक को संपादित करूँगा!
क्रिस्टोफ़ीड

1
graysoftinc.com/early-steps/the-evils-of-the-for-loop नया लिंक है, अब JEG2 की साइट ऑनलाइन वापस आ गई है।
पोनोमोलोस

30

आपका पहला उदाहरण,

@collection.each do |item|
  # do whatever
end

अधिक मुहावरेदार है । जबकि रूबी लूपिंग कंस्ट्रक्शन का समर्थन करता है जैसे forऔर while, ब्लॉक सिंटैक्स आमतौर पर पसंद किया जाता है।

एक और सूक्ष्म अंतर यह है कि forलूप के भीतर घोषित किया गया कोई भी वैरिएबल लूप के बाहर उपलब्ध होगा, जबकि एक इटरेटर ब्लॉक के भीतर प्रभावी रूप से निजी हैं।


whileऔर untilवास्तव में कुछ बहुत ही ठोस उपयोग हैं जिन्हें प्रत्येक के साथ प्रतिस्थापित नहीं किया जा सकता है जैसे कि अद्वितीय मान उत्पन्न करना या REPL के लिए।
अधिकतम

6

एक और अलग ..

number = ["one", "two", "three"]
 => ["one", "two", "three"] 

loop1 = []
loop2 = []

number.each do |c|
  loop1 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

for c in number
  loop2 << Proc.new { puts c }
end
 => ["one", "two", "three"] 

loop1[1].call
two
 => nil 

loop2[1].call
three
 => nil 

स्रोत: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-rry

अधिक स्पष्ट के लिए: http://www.ruby-forum.com/topic/179264#784884


2

ऐसा लगता है कि कोई अंतर नहीं है, नीचे forका उपयोग करता eachहै।

$ irb
>> for x in nil
>> puts x
>> end
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):1
>> nil.each {|x| puts x}
NoMethodError: undefined method `each' for nil:NilClass
    from (irb):4

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

for .. in .. लूप के दायरे के बाहर पुनरावृत्ति सेट करता है, इसलिए

for a in [1,2]
  puts a
end

aलूप समाप्त होने के बाद परिभाषित पत्तियां । जहां eachनहीं है। जो उपयोग करने के पक्ष में एक और कारण है each, क्योंकि अस्थायी चर एक छोटी अवधि रहता है।


1
वहाँ है (yjerem, ChristopheD और बायर्ड उल्लेख के रूप में) एक छोटा सा फर्क चर गुंजाइश के विषय में।
टेलीमेकस

गलत, नीचे का forउपयोग नहीं करता eachहै। अन्य उत्तर देखें।
आखुण

@akuhn आगे स्पष्टीकरण के लिए कृपया इस प्रश्न और इसके उत्कृष्ट उत्तर दोनों को देखें ।
सागर पंड्या

2

कभी भी उपयोग न करें forयह लगभग अप्राप्य बग पैदा कर सकता है।

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

यहाँ एक उदाहरण है जहाँ forबग का परिचय दिया जाता है,

class Library
  def initialize
    @ary = []
  end
  def method_with_block(&block)
    @ary << block
  end
  def method_that_uses_these_blocks
    @ary.map(&:call)
  end
end

lib = Library.new

for n in %w{foo bar quz}
  lib.method_with_block { n }
end

puts lib.method_that_uses_these_blocks

प्रिंटों

quz
quz
quz

%w{foo bar quz}.each { |n| ... }प्रिंट का उपयोग करना

foo
bar
quz

क्यों?

एक forलूप में चर nको केवल और केवल एक बार परिभाषित किया जाता है कि एक परिभाषा सभी पुनरावृत्तियों के लिए उपयोग की जाती है। इसलिए प्रत्येक ब्लॉक उसी का उल्लेख करता है nजिसमें quzलूप समाप्त होने के समय तक का मूल्य होता है। बग!

एक में eachपाश एक ताजा चर n, प्रत्येक यात्रा के लिए निर्धारित चर ऊपर के उदाहरण के लिए किया जाता है nतीन अलग-अलग बार परिभाषित किया गया है। इसलिए प्रत्येक ब्लॉक nसही मानों के साथ एक अलग को संदर्भित करता है।


0

जहाँ तक मुझे पता है, इन-लैंग्वेज कंट्रोल स्ट्रक्चर्स के बजाय ब्लॉक्स का उपयोग करना अधिक मुहावरेदार है।


0

मैं सिर्फ रूबी में लूप के लिए एक विशिष्ट बिंदु बनाना चाहता हूं। यह अन्य भाषाओं के समान एक निर्माण की तरह लग सकता है, लेकिन वास्तव में यह रूबी में हर दूसरे लूपिंग निर्माण की तरह एक अभिव्यक्ति है। वास्तव में, प्रत्येक पुनरावृत्त के रूप में असंख्य वस्तुओं के साथ काम करता है।

के लिए पारित संग्रह किसी भी वस्तु हो सकता है जिसमें प्रत्येक पुनरावृत्ति विधि हो। ऐरे और हैश प्रत्येक विधि को परिभाषित करते हैं, और कई अन्य रूबी ऑब्जेक्ट भी करते हैं। के लिए / लूप निर्दिष्ट ऑब्जेक्ट के प्रत्येक विधि को कॉल करता है। चूंकि यह इटैलर मान देता है, इसलिए लूप निर्दिष्ट चर (या चर) के लिए प्रत्येक मान (या मूल्यों के प्रत्येक सेट) को असाइन करता है और फिर शरीर में कोड निष्पादित करता है।

यह एक मूर्खतापूर्ण उदाहरण है, लेकिन इस बिंदु को दर्शाता है कि लूप के लिए किसी भी ऑब्जेक्ट के साथ काम करता है जिसमें प्रत्येक विधि है, ठीक उसी तरह जैसे कि प्रत्येक पुनरावृत्ति कैसे करता है:

class Apple
  TYPES = %w(red green yellow)
  def each
    yield TYPES.pop until TYPES.empty?
  end
end

a = Apple.new
for i in a do
  puts i
end
yellow
green
red
=> nil

और अब प्रत्येक पुनरावृति:

a = Apple.new
a.each do |i|
  puts i
end
yellow
green
red
=> nil

जैसा कि आप देख सकते हैं, दोनों प्रत्येक विधि का जवाब दे रहे हैं जो मानों को ब्लॉक में वापस लाती है। जैसा कि यहां सभी ने कहा है, लूप के लिए प्रत्येक इटेरेटर का उपयोग करना निश्चित रूप से बेहतर है। मैं बस इस बिंदु पर घर चलाना चाहता था कि लूप में कुछ भी जादुई नहीं है। यह एक अभिव्यक्ति है जो एक संग्रह की प्रत्येक विधि को आमंत्रित करता है और फिर इसे अपने कोड के ब्लॉक में भेजता है। इसलिए, यह एक बहुत ही दुर्लभ मामला है जिसके लिए आपको उपयोग करने की आवश्यकता होगी। प्रत्येक itter का उपयोग लगभग हमेशा (ब्लॉक स्कोप के अतिरिक्त लाभ के साथ) करें।


0
(1..4).each { |i| 


  a = 9 if i==3

  puts a 


}
#nil
#nil
#9
#nil

for i in 1..4

  a = 9 if i==3

  puts a

end
#nil
#nil
#9
#9

'लूप' के लिए, प्रत्येक लूप के बाद भी स्थानीय वेरिएबल रहता है। 'प्रत्येक' लूप में, लोकल वैरिएबल प्रत्येक लूप के बाद रिफ्रेश होता है।

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