web-dev-qa-db-fra.com

"pour" vs "each" dans Ruby

Je viens de poser une brève question sur les boucles en Ruby. Existe-t-il une différence entre ces deux manières de parcourir une collection?

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

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

Je me demandais simplement si elles étaient identiques ou s'il y avait peut-être une différence subtile (peut-être lorsque @collection est nul).

190
mportiz08

C'est la seule différence:

chaque:

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

pour:

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

Avec la boucle for, la variable itérateur subsiste après la fin du bloc. Avec la boucle each, ce n'est pas le cas, sauf si elle était déjà définie en tant que variable locale avant le début de la boucle.

Autre que cela, for est juste un sucre de syntaxe pour la méthode each.

Lorsque @collection est nil les deux boucles lisent une exception:

Exception: variable locale non définie ou méthode `@collection 'pour main: Object

301
Jeremy Ruten

Voir " Les maux de la boucle For " pour une bonne explication (il y a une petite différence en considérant la portée variable).

Utiliser each est considéré comme plus idiomatique utilisation de Ruby.

43
ChristopheD

Votre premier exemple,

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

est plus idiomatique . Bien que Ruby prenne en charge les constructions en boucle telles que for et while, la syntaxe de bloc est généralement préférable.

Une autre différence subtile est que toute variable que vous déclarez dans une boucle for sera disponible en dehors de la boucle, alors que celles contenues dans un bloc itérateur sont en réalité privées.

28
Bayard Randel

Un plus différent ..

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 

source: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-Ruby

pour plus de clarté: http://www.Ruby-forum.com/topic/179264#784884

6
Mr. Black

Il semble qu'il n'y ait pas de différence, for utilise each dessous.

$ 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

Comme le dit Bayard, chacun est plus idiomatique. Il vous cache plus de choses et ne nécessite aucune fonctionnalité linguistique particulière. Commentaire de Télémaque

for .. in .. place l'itérateur en dehors de la portée de la boucle, donc

for a in [1,2]
  puts a
end

laisse a défini une fois la boucle terminée. Où comme each pas. Ce qui est une autre raison en faveur de l'utilisation de each, car la variable temp vit une période plus courte.

2
BaroqueBobcat

Ne jamais utiliser for cela peut causer des bugs.

La différence est subtile mais peut causer d’énormes insectes!

Ne soyez pas dupe, il ne s'agit pas de code idiomatique ni de problèmes de style. Il s’agit d’éviter des bogues presque introuvables dans le code de production. L'implémentation de Ruby dans for présente un grave défaut et ne doit pas être utilisée. Toujours utiliser les boucles each, ne jamais utiliser la boucle for.

Voici un exemple où for introduit un bogue,

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

Impressions

quz
quz
quz

Utiliser %w{foo bar quz}.each { |n| ... } imprime

foo
bar
quz

Pourquoi?

Dans une boucle for, la variable n est définie une fois et une seule fois, puis cette définition est utilisée pour toutes les itérations. Par conséquent, chaque bloc fait référence au même n qui a une valeur de quz au moment où la boucle se termine. Punaise!

Dans une boucle each, une nouvelle variable n est définie pour chaque itération, par exemple, au-dessus de la variable n est définie trois fois. Par conséquent, chaque bloc fait référence à un n séparé avec les valeurs correctes.

1
akuhn

Je veux juste faire un point particulier sur la boucle in dans Ruby. Cela peut sembler une construction similaire à d'autres langages, mais en réalité c'est une expression comme toutes les autres constructions en boucle de Ruby. En fait, le for in fonctionne avec les objets Enumerable comme chaque itérateur.

La collection transmise à pour dans peut être n'importe quel objet ayant une méthode de chaque itérateur. Les tableaux et les hachages définissent chaque méthode, ainsi que de nombreux autres objets Ruby. La boucle for/in appelle chaque méthode de l'objet spécifié. Lorsque cet itérateur génère des valeurs, la boucle for affecte chaque valeur (ou chaque ensemble de valeurs) à la variable spécifiée (ou aux variables), puis exécute le code dans le corps.

Ceci est un exemple stupide, mais illustre le fait que la boucle for in fonctionne avec TOUS les objets ayant une méthode, tout comme le fait chaque itérateur:

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

Et maintenant, chaque itérateur:

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

Comme vous pouvez le constater, les deux répondent à chaque méthode, ce qui renvoie des valeurs au bloc. Comme tout le monde l'a dit, il est nettement préférable d'utiliser chaque itérateur plutôt que la boucle in. Je voulais juste souligner le fait qu'il n'y a rien de magique dans la boucle. C'est une expression qui appelle chaque méthode d'une collection et la transmet ensuite à son bloc de code. Par conséquent, il est très rare que vous deviez utiliser for in. Utilisez presque toujours chaque itérateur (avec l'avantage supplémentaire de la portée de bloc).

0
Donato

Autant que je sache, l'utilisation de blocs au lieu de structures de contrôle dans le langage est plus idiomatique.

0
Jonathan Sterling
(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

Dans la boucle 'for', la variable locale est toujours vivante après chaque boucle. Dans "chaque" boucle, la variable locale est actualisée après chaque boucle.

0
Kelvin Tan