2010-07-20 4 views
150

Ich hatte gerade eine kurze Frage bezüglich Schleifen in Ruby. Gibt es einen Unterschied zwischen diesen beiden Möglichkeiten, eine Sammlung zu durchlaufen?"für" vs "jeder" in Ruby

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

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

nur fragen, ob diese genau gleich sind oder wenn vielleicht gibt es einen feinen Unterschied (möglicherweise, wenn @collection ist null).

Antwort

249

Dies ist der einzige Unterschied:

jeweils:

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 

für:

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

Mit der for Schleife lebt die Iteratorvariable noch nach dem Block erledigt. Mit der each-Schleife ist dies nicht der Fall, es sei denn, es wurde bereits vor dem Start der Schleife als lokale Variable definiert.

Ansonsten ist for nur Syntax Zucker für die each Methode.

Wenn @collection ist nil beide Schleifen werfen eine Ausnahme:

Ausnahme: undefined lokale Variable oder Methode `@collection‘ für Haupt: Object

25

Ihr erstes Beispiel

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

is more idiomatic. Während Ruby Looping-Konstrukte wie for und while unterstützt, wird die Block-Syntax im Allgemeinen bevorzugt.

Ein weiterer feiner Unterschied besteht darin, dass jede Variable, die Sie innerhalb einer for-Schleife deklarieren, außerhalb der Schleife verfügbar ist, während solche innerhalb eines Iteratorblocks effektiv privat sind.

0

Soweit ich weiß, ist die Verwendung von Blöcken anstelle von in-Sprache-Kontrollstrukturen mehr idiomatisch.

2

Es sieht aus als gäbe es keinen Unterschied, for verwendet each darunter.

$ 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 mehr idiomatisch. Es verbirgt sich mehr vor Ihnen und erfordert keine speziellen Sprachfunktionen. Per Telemachos Kommentar des

for .. in .. setzt den Iterator außerhalb des Umfangs der Schleife, so

for a in [1,2] 
    puts a 
end 

verlässt a definiert, nachdem die Schleife beendet ist. Wo wie each nicht. Dies ist ein weiterer Grund für die Verwendung von each, weil die Variable Temp dauert eine kürzere Zeit.

+1

Dort ist ein kleiner Unterschied (wie Yerem, ChristopheD und Bayard erwähnen) bezüglich des variablen Umfangs. – Telemachus

+0

Falsch, 'for' verwendet nicht' each' darunter. Siehe andere Antworten. – akuhn

+0

@akuhn Zur weiteren Klärung siehe bitte [question] (http://stackoverflow.com/q/39839809/5101493) und die beiden ausgezeichneten Antworten. –

41

Siehe "The Evils of the For Loop" für eine gute Erklärung (es gibt einen kleinen Unterschied in Bezug auf variable Scoping).

Verwenden each ist considered more idiomatic Verwendung von Ruby.

+0

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

+1

http://graysoftinc.com/early-steps/the-evils-of-the-for-loop ist der neue Link, jetzt, wo die JEG2-Seite wieder online ist. – pnomolos

5

Eine andere mehr ..

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 

Quelle: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-ruby

für mehr clear: http://www.ruby-forum.com/topic/179264#784884

+0

'loop1' und' loop2' sind nicht definiert. –

+0

Antwort aktualisiert! Danke @ DarekNędza –

0

Nie verwenden Sie for es kann Fehler verursachen.

Der Unterschied ist subtil, kann aber große Fehler verursachen!

Lassen Sie sich nicht täuschen, es geht nicht um idiomatische Code- oder Stilprobleme. Dies ist eine Frage der Vermeidung fast unauffindbarer Fehler im Produktionscode. Rubys Implementierung von for weist einen schwerwiegenden Fehler auf und sollte nicht verwendet werden. Verwenden Sie immer each Loops, niemals for Schleife verwenden. Hier

ist ein Beispiel, wo for einen Fehler einführt,

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 

Drucke

quz 
quz 
quz 

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

foo 
bar 
quz 

Warum? In einer for-Schleife wird die Variable n nur einmal definiert und dann wird diese eine Definition für alle Iterationen verwendet. Daher beziehen sich alle Blöcke auf denselben n, der zum Zeitpunkt des Schleifenendes einen Wert von quz hat. Fehler!

In einer each Schleife wird für jede Iteration eine neue Variable n definiert, zum Beispiel wird oberhalb der Variablen n drei separate Zeiten definiert. Daher bezieht sich jeder Block auf einen separaten n mit den korrekten Werten.

+0

Sie meinen 'method', nicht' function'. –

+0

@ericduminil guter Fang, auch polyglott, haha. – akuhn