Ruby'de “for” ve “each”


200

Ruby'deki döngüler hakkında kısa bir sorum vardı. Bir koleksiyon yoluyla bu iki yineleme yöntemi arasında bir fark var mı?

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

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

Bunların tamamen aynı olup olmadığını veya belki de ince bir fark olup olmadığını merak ediyorum (muhtemelen @collectionnil olduğunda ).

Yanıtlar:


315

Tek fark bu:

her biri:

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

için:

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

İle forblok yapıldıktan sonra döngü, yineleyici değişken hala yaşıyor. İleeachDöngü döngü başlamadan önce zaten yerel bir değişken olarak tanımlanmadığı sürece, bunu yapmaz.

Bunun dışında for, eachyöntem için sadece sözdizimi şekeri .

Ne zaman @collectionolduğunu nilhem döngüler bir istisna:

İstisna: main için tanımsız yerel değişken veya yöntem `@collection ': Nesne


3
x'in durumda kalmasının iyi bir nedeni var mı veya bu kötü tasarım mı: P? Bana öyle geliyor ki bu diğer dillerle karşılaştırıldığında oldukça sezgisel değil.
cyc115

3
@ cyc115 sebebi xde kalıntıları için senaryo (genel anlamda) anahtar kelimelerin yeni kapsamları oluşturmaz olmasından kaynaklanmaktadır. eğer , sürece , başlamak , için , süre , vb geçerli kapsamı ile bütün işler. #eachancak bir bloğu kabul eder. Bloklar her zaman geçerli kapsamın üstüne kendi kapsamını ekler. Bu, blokta yeni bir değişken (bu nedenle yeni bir kapsam) bildirilmesine, ek kapsam bulunmadığından blok dışından erişilemeyeceği anlamına gelir.
3limin4t0r

43

İyi bir açıklama için bkz. " Döngü İçin Kötüler " (değişken kapsamı göz önünde bulundurmada küçük bir fark vardır).

Kullanmak Ruby'nin daha deyimsel kullanımı eacholarak kabul edilir .


@zachlatta: Bildirdiğiniz için teşekkürler. Bağlantıyı makalenin bir webarchive.org değişkenine işaret edecek şekilde düzenleyeceğim!
ChristopheD

1
graysoftinc.com/early-steps/the-evils-of-the-for-loop yeni bağlantı, şimdi JEG2'nin sitesi tekrar çevrimiçi.
pnomolos

30

İlk örneğiniz,

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

daha deyimsel . Ruby forve gibi döngü yapılarını desteklese de while, blok sözdizimi genellikle tercih edilir.

Bir başka ince fark, bir fordöngü içinde bildirdiğiniz herhangi bir değişkenin döngü dışında kullanılabilir olması, ancak bir yineleyici bloğundaki değişkenlerin etkili bir şekilde özel olmasıdır.


whileve untilaslında benzersiz değerler üretmek veya REPL'ler gibi her biri ile değiştirilemeyecek bazı somut kullanımlara sahip olmak.
en fazla


2

Fark yok gibi görünüyor, altında forkullanıyor 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

Bayard'ın dediği gibi, her biri daha deyimsel. Sizden daha fazlasını gizler ve özel dil özellikleri gerektirmez. Per Telemachus adlı kullanıcının yorumu

for .. in .. yineleyiciyi döngünün kapsamı dışında tutar,

for a in [1,2]
  puts a
end

adöngü bittikten sonra tanımlanan yapraklar . Nerede eacholmadığı gibi. Bu, eachgeçici değişkenin daha kısa bir süre yaşadığı için kullanmaktan başka bir nedendir .


1
Orada olan değişken kapsamı ile ilgili (yjerem, ChristopheD ve Bayard söz gibi) küçük bir fark vardır.
Telemachus

Yanlış, altında forkullanmaz each. Diğer cevaplara bakın.
akuhn

@akuhn Daha fazla açıklama için lütfen bu soruya ve her ikisinin de mükemmel yanıtlarına bakın.
Sagar Pandya

2

Asla kullanmayın for, neredeyse takip edilemeyen hatalara neden olabilir.

Kanmayın, bu deyimsel kod veya stil sorunları ile ilgili değildir. Ruby'nin uygulanmasının forciddi bir kusuru vardır ve kullanılmamalıdır.

İşte forbir hata tanıtan bir örnek ,

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

Baskılar

quz
quz
quz

%w{foo bar quz}.each { |n| ... }Baskıları kullanma

foo
bar
quz

Neden?

Bir fordöngüde, değişken nsadece bir kez tanımlanır ve daha sonra bu tanım tüm tekrarlamalar için kullanılır. Bu nedenle her blok , ilmek bitene kadar nbir değere sahip olanı ifade eder quz. Böcek!

Bir eachdöngüde nher yineleme için yeni bir değişken tanımlanır, örneğin değişkenin üstünde nüç ayrı kez tanımlanır. Bu nedenle, her blok ndoğru değerlere sahip bir ayrıma karşılık gelir .



0

Sadece Ruby'deki for in loop hakkında belirli bir noktaya değinmek istiyorum. Diğer dillere benzer bir yapı gibi görünebilir, ancak aslında Ruby'deki diğer tüm döngü yapıları gibi bir ifadedir. Aslında, for, her yineleyici gibi Numaralandırılabilir nesnelerle çalışır.

For için iletilen koleksiyon, her bir yineleyici yöntemine sahip herhangi bir nesne olabilir. Diziler ve karmalar her yöntemi tanımlar ve diğer birçok Ruby nesnesi de bunu yapar. For / in döngüsü belirtilen nesnenin her yöntemini çağırır. Yineleyici değer verdiğinden, for döngüsü her bir değeri (veya her bir değer kümesini) belirtilen değişkene (veya değişkenlere) atar ve daha sonra kodu gövdede yürütür.

Bu aptalca bir örnektir, ancak for in loop'un her bir yönteme sahip HERHANGİ bir nesne ile çalıştığı noktayı gösterir, tıpkı her yineleyicinin yaptığı gibi:

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

Ve şimdi her bir yineleyici:

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

Gördüğünüz gibi, her ikisi de bloğa değer veren her yönteme yanıt veriyor. Buradaki herkesin belirttiği gibi, her bir yineleyiciyi for-in döngüsü üzerinden kullanmak kesinlikle tercih edilir. Ben sadece for in loop hakkında büyülü bir şey olmadığı noktaya götürmek istedim. Bir koleksiyonun her yöntemini çağıran ve daha sonra kod bloğuna ileten bir ifadedir. Bu nedenle, içinde kullanmanız gereken çok nadir bir durumdur. Her yineleyiciyi neredeyse her zaman kullanın (blok kapsamının ek yararı ile).


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

'For' döngüsünde, yerel değişken hala her döngüden sonra yaşar. 'Each' döngüsünde, yerel değişken her döngüden sonra yenilenir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.