2010-07-20 12 views
150

Ho appena avuto una breve domanda riguardante i loop in Ruby. C'è una differenza tra questi due modi di iterare attraverso una collezione?"for" vs "each" in Ruby

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

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

chiedo solo se questi sono esattamente gli stessi o se forse c'è una sottile differenza (possibilmente quando @collection è pari a zero).

risposta

249

Questa è l'unica differenza:

ogni:

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 

Per:

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

Con il ciclo for, la variabile di iterazione vive ancora dopo il blocco è fatto. Con il ciclo each, non lo fa, a meno che non sia già stato definito come variabile locale prima dell'avvio del ciclo.

A parte questo, for è solo zucchero di sintassi per il metodo each.

Quando @collection è nil entrambi i loop gettare un'eccezione:

Eccezione: non definito variabile locale o metodo `@collection' per principale: Oggetto

25

Il tuo primo esempio,

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

is more idiomatic. Mentre Ruby supporta i costrutti di loop come for e while, la sintassi del blocco è generalmente preferita.

Un'altra sottile differenza è che qualsiasi variabile dichiarata all'interno di un ciclo for sarà disponibile al di fuori del ciclo, mentre quelle all'interno di un blocco iteratore sono effettivamente private.

0

Per quanto ne so, utilizzare i blocchi anziché le strutture di controllo nella lingua è più idiomatico.

2

Sembra che non ci siano differenze, for utilizza each sotto.

Come dice Bayard, ciascuno è più idiomatico. Ti nasconde di più e non richiede caratteristiche linguistiche speciali. di Per Telemaco commento

for .. in .. imposta l'iteratore al di fuori del campo di applicazione del ciclo, in modo da

for a in [1,2] 
    puts a 
end 

lascia a definito dopo il ciclo è finito. Dove come each no. Quale è un altro motivo a favore dell'uso di each, perché la variabile temp ha una durata inferiore.

+1

C'è una piccola differenza (come menzionano yjerem, ChristopheD e Bayard) in merito alla portata variabile. – Telemachus

+0

errato, 'for' non usa' each' sotto. Vedi altre risposte. – akuhn

+0

@akuhn Per ulteriori chiarimenti, si prega di consultare questa [domanda] (http://stackoverflow.com/q/39839809/5101493) ed entrambe le sue eccellenti risposte. –

41

Vedere "The Evils of the For Loop" per una buona spiegazione (c'è una piccola differenza considerando l'ambito variabile).

Utilizzo di each è considered more idiomatic uso di Ruby.

+0

@zachlatta: Grazie per la notifica. Modificherò il link per puntare a una variante di webarchive.org dell'articolo! – ChristopheD

+1

http://graysoftinc.com/early-steps/the-evils-of-the-for-loop è il nuovo collegamento, ora che il sito di JEG2 è di nuovo online. – pnomolos

5

Ancora una diversa ..

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 

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

per ulteriori chiari: http://www.ruby-forum.com/topic/179264#784884

+0

'loop1' e' loop2' non sono definiti. –

+0

Risposta aggiornata! Grazie @ DarekNędza –

0

Mai usare mai for può causare bug.

La differenza è sottile ma può causare tremendi bug!

Non fatevi ingannare, non si tratta di codice idiomatico o di problemi di stile. Si tratta di evitare bug quasi irreperibili nel codice di produzione. L'implementazione di Ruby di for ha un difetto grave e non dovrebbe essere utilizzata. Utilizzare sempre i loop each, non utilizzare mai il ciclo for.

Ecco un esempio in cui for introduce un bug,

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 

Stampe

quz 
quz 
quz 

Uso %w{foo bar quz}.each { |n| ... } stampe

foo 
bar 
quz 

Perché?

In un ciclo for la variabile n viene definita una sola volta e quindi quella definizione viene utilizzata per tutte le iterazioni. Quindi ogni blocco si riferisce allo stesso n che ha un valore di quz al termine del ciclo. Bug!

In un ciclo each una nuova variabile n viene definita per ogni iterazione, ad esempio sopra la variabile n viene definita tre volte separate. Quindi ogni blocco si riferisce a un separato n con i valori corretti.

+0

Vuoi dire 'metodo', non' funzione'. –

+0

@ericduminil buona cattura, anche poliglotta, haha. – akuhn