"For" vs "each" in Ruby


200

Ho appena fatto una domanda veloce riguardo ai loop in Ruby. C'è una differenza tra questi due modi di scorrere una raccolta?

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

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

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

Risposte:


315

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 forloop, la variabile iteratore rimane attiva anche dopo il blocco. Con il eachciclo no, a meno che non sia già stato definito come variabile locale prima dell'inizio del ciclo.

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

Quando @collectionè nilentrambi i loop generano un'eccezione:

Eccezione: variabile locale non definita o metodo `@collection 'per main: Object


3
c'è una buona ragione per cui x rimane nel caso o è questo cattivo design: P? Mi sembra che questo sia piuttosto poco intuitivo rispetto alla maggior parte delle altre lingue.
cyc115,

3
@ cyc115 Il motivo per cui xrimane nello scenario for è dovuto al fatto che le parole chiave (in generale) non creano nuovi ambiti. se , a meno che , cominciare , per , mentre , ecc tutto il lavoro con l'ambito corrente. #eachaccetta comunque un blocco. I blocchi aggiungono sempre il proprio ambito sopra l'ambito corrente. Ciò significa che la dichiarazione di una nuova variabile nel blocco (quindi un nuovo ambito) non sarà accessibile dall'esterno del blocco poiché tale ambito aggiuntivo non è disponibile lì.
3limin4t0r


30

Il tuo primo esempio,

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

è più idiomatico . Mentre Ruby supporta costrutti di loop come fore while, la sintassi del blocco è generalmente preferita.

Un'altra sottile differenza è che qualsiasi variabile dichiarata all'interno di un forloop sarà disponibile all'esterno del loop, mentre quelli all'interno di un blocco iteratore sono effettivamente privati.


whilee in untilrealtà hanno alcuni usi molto concreti che non possono essere sostituiti con ciascuno come ad esempio la generazione di valori univoci o per REPL.
massimo


2

Sembra che non ci siano differenze, forusi eachsotto.

$ 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

Come dice Bayard, ognuno è più idiomatico. Ti nasconde di più e non richiede funzionalità linguistiche speciali. Per il commento di Telemaco

for .. in .. imposta l'iteratore al di fuori dell'ambito del ciclo, quindi

for a in [1,2]
  puts a
end

foglie adefinite al termine del ciclo. Dove eachno. Questo è un altro motivo a favore dell'uso each, perché la variabile temp vive un periodo più breve.


1
V'è una piccola differenza (come yjerem, ChristopheD e Bayard menzione) per quanto riguarda la portata variabile.
Telemaco,

Errato, fornon utilizza al di eachsotto. Vedi altre risposte.
Akuhn,

@akuhn Per ulteriori chiarimenti, consultare questa domanda e le sue eccellenti risposte.
Sagar Pandya,

2

Mai e poi mai usarlo forpuò causare bug quasi non rintracciabili.

Non farti ingannare, non si tratta di codice idiomatico o problemi di stile. L'implementazione di Ruby forha un grave difetto e non deve essere utilizzata.

Ecco un esempio in cui forintroduce 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

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

foo
bar
quz

Perché?

In un forciclo la variabile nviene definita una sola volta e quindi viene utilizzata una definizione per tutte le iterazioni. Quindi ogni blocco si riferisce allo stesso nche ha un valore diquz entro la fine del ciclo. Bug!

In un eachciclo una nuova variabilen viene definita per ogni iterazione, ad esempio sopra la variabile nviene definita tre volte separate. Quindi ogni blocco si riferisce ad un separato ncon i valori corretti.


0

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


0

Voglio solo fare un punto specifico sul ciclo for in di Ruby. Potrebbe sembrare un costrutto simile ad altre lingue, ma in realtà è un'espressione come ogni altro costrutto in loop in Ruby. In effetti, for in funziona con oggetti Enumerable proprio come ogni iteratore.

La raccolta passata a per in può essere qualsiasi oggetto che ha un metodo per ogni iteratore. Le matrici e gli hash definiscono ciascun metodo, così come molti altri oggetti di Ruby. Il ciclo for / in chiama ciascun metodo dell'oggetto specificato. Poiché l'iteratore restituisce valori, il ciclo for assegna ciascun valore (o ogni set di valori) alla variabile (o variabili) specificata e quindi esegue il codice nel corpo.

Questo è un esempio sciocco, ma illustra il punto in cui il ciclo for in funziona con QUALSIASI oggetto che ha un metodo ogni, proprio come fa ogni iteratore:

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

E ora ogni iteratore:

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

Come puoi vedere, entrambi stanno rispondendo a ciascun metodo che restituisce valori al blocco. Come tutti qui affermano, è sicuramente preferibile utilizzare ciascun iteratore sopra il ciclo for in. Volevo solo portare a casa il punto che non c'è nulla di magico nel for in loop. È un'espressione che invoca ciascun metodo di una raccolta e quindi lo passa al suo blocco di codice. Quindi, è un caso molto raro che dovresti usare per in. Usa ogni iteratore quasi sempre (con l'ulteriore vantaggio dell'ambito del blocco).


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

Nel ciclo 'for', la variabile locale è ancora viva dopo ogni ciclo. Nel ciclo 'ogni', la variabile locale si aggiorna dopo ogni ciclo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.