"Für" gegen "jeder" in Ruby


200

Ich hatte gerade eine kurze Frage zu Loops in Ruby. Gibt es einen Unterschied zwischen diesen beiden Arten, eine Sammlung zu durchlaufen?

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

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

Ich frage mich nur, ob diese genau gleich sind oder ob es vielleicht einen subtilen Unterschied gibt (möglicherweise wann @collectionist null).

Antworten:


315

Dies ist der einzige Unterschied:

jeder:

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

zum:

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

Mit der forSchleife lebt die Iteratorvariable noch, nachdem der Block fertig ist. Bei der eachSchleife ist dies nicht der Fall, es sei denn, sie wurde bereits vor dem Start der Schleife als lokale Variable definiert.

Davon abgesehen forist nur Syntaxzucker für die eachMethode.

Wann @collectionlösen nilbeide Schleifen eine Ausnahme:

Ausnahme: undefinierte lokale Variable oder Methode `@collection 'für main: Object


3
Gibt es einen guten Grund, warum x im for-Fall bleibt, oder ist dieses schlechte Design: P? Mir scheint, dass dies im Vergleich zu den meisten anderen Sprachen eher unintuitiv ist.
Cyc115

3
@ cyc115 Der Grund, warum xim for- Szenario verbleibt , liegt in der Tatsache, dass (im Allgemeinen) Schlüsselwörter keine neuen Bereiche erstellen. Wenn , es sei denn , beginnen , für , während usw., arbeiten alle mit dem aktuellen Bereich. #eachakzeptiert jedoch einen Block. Blöcke fügen immer einen eigenen Bereich über dem aktuellen Bereich hinzu. Dies bedeutet, dass das Deklarieren einer neuen Variablen im Block (also eines neuen Bereichs) von außerhalb des Blocks nicht zugänglich ist, da dieser zusätzliche Bereich dort nicht verfügbar ist.
3limin4t0r

43

Eine gute Erklärung finden Sie unter " Die Übel der For-Schleife " (es gibt einen kleinen Unterschied in Bezug auf das variable Scoping).

Die Verwendung eachwird als idiomatischere Verwendung von Ruby angesehen.


@ Zachlatta: Danke für die Benachrichtigung. Ich werde den Link bearbeiten, um auf eine webarchive.org-Variante des Artikels zu verweisen!
ChristopheD

1
graysoftinc.com/early-steps/the-evils-of-the-for-loop ist der neue Link, jetzt, da die Website von JEG2 wieder online ist.
Pnomolos

29

Dein erstes Beispiel,

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

ist idiomatischer . Während Ruby Schleifenkonstrukte wie forund unterstützt while, wird die Blocksyntax im Allgemeinen bevorzugt.

Ein weiterer subtiler Unterschied besteht darin, dass jede Variable, die Sie innerhalb einer forSchleife deklarieren , außerhalb der Schleife verfügbar ist, während die Variablen innerhalb eines Iteratorblocks praktisch privat sind.


whileund untilhaben tatsächlich einige sehr konkrete Verwendungen, die nicht durch jede ersetzt werden können, wie zum Beispiel das Generieren eindeutiger Werte oder für REPLs.
Max


2

Es sieht so aus, als gäbe es keinen Unterschied, forverwendet eachdarunter.

$ 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

Wie Bayard sagt, ist jeder idiomatischer. Es verbirgt sich mehr vor Ihnen und erfordert keine speziellen Sprachfunktionen. Per Kommentar von Telemachos

for .. in .. Setzt den Iterator außerhalb des Bereichs der Schleife

for a in [1,2]
  puts a
end

Blätter adefiniert, nachdem die Schleife beendet ist. Wo wie eachnicht. Dies ist ein weiterer Grund für die Verwendung each, da die temporäre Variable eine kürzere Lebensdauer hat.


1
Es gibt einen kleinen Unterschied (wie Yjerem, ChristopheD und Bayard erwähnen) hinsichtlich des variablen Umfangs.
Telemachos

Falsch, forwird nicht eachdarunter verwendet. Siehe andere Antworten.
Akuhn

@akuhn Zur weiteren Klärung lesen Sie bitte diese Frage und ihre beiden ausgezeichneten Antworten.
Sagar Pandya

2

Niemals verwenden, da fordies fast unauffindbare Fehler verursachen kann.

Lassen Sie sich nicht täuschen, hier geht es nicht um idiomatische Code- oder Stilprobleme. Rubys Implementierung von forhat einen schwerwiegenden Fehler und sollte nicht verwendet werden.

Hier ist ein Beispiel, in dem forein Fehler eingeführt wird:

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

Druckt

quz
quz
quz

Mit %w{foo bar quz}.each { |n| ... }Druck

foo
bar
quz

Warum?

In einer forSchleife wird die Variable neinmal definiert und dann wird diese eine Definition für alle Iterationen verwendet. Daher beziehen sich alle Blöcke auf denselben, nder quzzum Zeitpunkt des Endes der Schleife einen Wert von hat . Fehler!

In einer eachSchleife nwird für jede Iteration eine neue Variable definiert, beispielsweise wird die Variable oben ndrei Mal getrennt definiert. Daher bezieht sich jeder Block auf einen separaten nmit den richtigen Werten.


0

Soweit ich weiß, ist die Verwendung von Blöcken anstelle von Kontrollstrukturen in der Sprache idiomatischer.


0

Ich möchte nur einen bestimmten Punkt über die for in-Schleife in Ruby ansprechen. Es mag wie ein Konstrukt erscheinen, das anderen Sprachen ähnlich ist, aber tatsächlich ist es ein Ausdruck wie jedes andere Schleifenkonstrukt in Ruby. Tatsächlich funktioniert das for in mit Enumerable-Objekten genauso wie jeder Iterator.

Die an for in übergebene Auflistung kann ein beliebiges Objekt sein, das über eine Iteratormethode verfügt. Arrays und Hashes definieren jede Methode, und viele andere Ruby-Objekte auch. Die for / in-Schleife ruft jede Methode des angegebenen Objekts auf. Da dieser Iterator Werte liefert, weist die for-Schleife jeden Wert (oder jeden Wertesatz) der angegebenen Variablen (oder Variablen) zu und führt dann den Code im Hauptteil aus.

Dies ist ein dummes Beispiel, veranschaulicht jedoch den Punkt, an dem die for in-Schleife mit JEDEM Objekt funktioniert, das über jede Methode verfügt, genau wie der jeweilige Iterator:

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

Und jetzt jeder Iterator:

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

Wie Sie sehen können, reagieren beide auf jede Methode, die dem Block Werte zurückgibt. Wie alle hier angegeben haben, ist es definitiv vorzuziehen, jeden Iterator gegenüber der for in-Schleife zu verwenden. Ich wollte nur den Punkt nach Hause fahren, dass die for-in-Schleife nichts Magisches ist. Es ist ein Ausdruck, der jede Methode einer Sammlung aufruft und sie dann an ihren Codeblock übergibt. Daher ist es ein sehr seltener Fall, den Sie für in verwenden müssten. Verwenden Sie fast immer jeden Iterator (mit dem zusätzlichen Vorteil des Blockumfangs).


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

In der 'for'-Schleife lebt die lokale Variable nach jeder Schleife noch. In jeder Schleife wird die lokale Variable nach jeder Schleife aktualisiert.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.