"for "vs" each " en Ruby


Acabo de tener una pregunta rápida con respecto a los bucles en Ruby. ¿Hay alguna diferencia entre estas dos formas de iterar a través de una colección?

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

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

Solo me pregunto si estos son exactamente los mismos o si tal vez hay una diferencia sutil (posiblemente cuando @collection es cero).

Author: the Tin Man, 2010-07-21

7 answers

Esta es la única diferencia:

Cada uno:

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

Donde dice:

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

Con el bucle for, la variable iteradora aún vive después de que se realiza el bloque. Con el bucle each, no lo hace, a menos que ya estuviera definido como una variable local antes de que comenzara el bucle.

Aparte de eso, for es solo azúcar de sintaxis para el método each.

Cuando @collection es nil ambos bucles lanzan una excepción:

Excepción: local indefinido variable o método ` @ collection ' para main: Object

 276
Author: Jeremy Ruten,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-09-24 07:17:00

Ver " Los males del Bucle For" para una buena explicación (hay una pequeña diferencia considerando el alcance variable).

Usar eaches considerado más idiomático el uso de Ruby.

 42
Author: ChristopheD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-09 14:10:42

Su primer ejemplo,

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

Es más idiomático. Mientras que Ruby soporta construcciones de bucle como for y while, generalmente se prefiere la sintaxis de bloque.

Otra diferencia sutil es que cualquier variable que declare dentro de un bucle for estará disponible fuera del bucle, mientras que aquellas dentro de un bloque iterador son efectivamente privadas.

 25
Author: Bayard Randel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-09 13:16:36

Uno más diferente..

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 

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

Para más claro: http://www.ruby-forum.com/topic/179264#784884

 6
Author: Mr. Black,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-05-03 19:47:02

Parece que no hay diferencia, for usa each debajo.

$ 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

Como dice Bayard, cada uno es más idiomático. Oculta más de usted y no requiere características especiales de lenguaje. Según el comentario de Telémaco

for .. in .. establece el iterador fuera del alcance del bucle, por lo que

for a in [1,2]
  puts a
end

Deja a definido después de terminar el bucle. Donde as each no lo hace. Que es otra razón a favor de usar each, porque la variable temp vive un tiempo más corto periodo.

 2
Author: BaroqueBobcat,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-20 22:10:40

Nunca use for puede causar errores.

La diferencia es sutil, pero puede causar errores tremendos!

No se deje engañar, esto no se trata de código idiomático o problemas de estilo. Se trata de evitar errores casi imposibles de rastrear en el código de producción. La implementación de Ruby de for tiene un grave defecto y no debe ser utilizada. Siempre use bucles each, nunca use bucles for.

Aquí hay un ejemplo donde 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

Impresiones

quz
quz
quz

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

foo
bar
quz

¿Por qué?

En un bucle for la variable n se define una sola vez y luego esa definición se usa para todas las iteraciones. Por lo tanto, cada bloque se refiere al mismo n que tiene un valor de quz cuando termina el bucle. ¡Bug!

En un bucle each se define una nueva variable n para cada iteración, por ejemplo, por encima de la variable n se define tres veces separadas. Por lo tanto cada bloque se refiere a un n separado con los valores correctos.

 1
Author: akuhn,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-12-23 23:43:38

Por lo que sé, usar bloques en lugar de estructuras de control en el lenguaje es más idiomático.

 0
Author: Jonathan Sterling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-07-20 21:31:23