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).
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
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.
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.
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
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.
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.
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).
Autant que je sache, l'utilisation de blocs au lieu de structures de contrôle dans le langage est plus idiomatique.
(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.