"For" vs "each" em Ruby


200

Acabei de fazer uma pergunta rápida sobre loops no Ruby. Existe uma diferença entre essas duas maneiras de iterar através de uma coleção?

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

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

Basta saber se estes são exatamente os mesmos ou se talvez haja uma diferença sutil (possivelmente quando @collectioné nulo).

Respostas:


315

Esta é a única diferença:

cada:

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

para:

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

Com o forloop, a variável iteradora ainda permanece após o término do bloco. Com o eachloop, ele não funciona, a menos que já tenha sido definido como uma variável local antes do início do loop.

Fora isso, foré apenas açúcar de sintaxe para o eachmétodo.

Quando @collectioné nilambos os loops lançar uma exceção:

Exceção: variável local indefinida ou método `@collection 'para main: Object


3
existe uma boa razão pela qual x permanece no caso for ou esse projeto é ruim: P? Parece-me que isso é pouco intuitivo em comparação com a maioria dos outros idiomas.
18136 Cyc115

3
@ cyc115 A razão pela qual xpermanece no para cenário é devido ao fato de que (em geral) palavras-chave não criar novos escopos. se , a menos que , comece , por , enquanto etc. tudo funcione com o escopo atual. #eachno entanto aceita um bloco. Os blocos sempre adicionam seu próprio escopo sobre o escopo atual. Significando que declarar uma nova variável no bloco (portanto, um novo escopo) não será acessível de fora do bloco, pois esse escopo adicional não está disponível lá.
3limin4t0r


30

Seu primeiro exemplo,

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

é mais idiomático . Enquanto Ruby suporta construções de loop como fore while, a sintaxe do bloco é geralmente preferida.

Outra diferença sutil é que qualquer variável que você declara dentro de um forloop estará disponível fora do loop, enquanto as de um bloco iterador são efetivamente privadas.


whilee untilrealmente tem alguns usos muito concretos que não podem ser substituídos por cada um, como por exemplo, gerar valores exclusivos ou para REPLs.
max


2

Parece que não há diferença, forusa eachpor baixo.

$ 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 diz Bayard, cada um é mais idiomático. Ele esconde mais de você e não requer recursos especiais de idioma. Por comentário de Telêmaco

for .. in .. define o iterador fora do escopo do loop, portanto

for a in [1,2]
  puts a
end

folhas adefinidas após o término do loop. Onde como eachnão. Qual é outro motivo a favor do uso each, porque a variável temp vive um período mais curto.


1
Não é uma pequena diferença (como yjerem, ChristopheD e Bayard menção) sobre o escopo de variáveis.
Telêmaco

Incorreto, fornão usa eachpor baixo. Veja outras respostas.
akuhn

@akuhn Para mais esclarecimentos, consulte esta pergunta e suas excelentes respostas.
Sagar Pandya

2

Nunca usá- forlo pode causar erros quase não rastreáveis.

Não se deixe enganar, não se trata de código idiomático ou de problemas de estilo. A implementação de Ruby fortem uma falha séria e não deve ser usada.

Aqui está um exemplo em que forintroduz um 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

Impressões

quz
quz
quz

Usando %w{foo bar quz}.each { |n| ... }impressões

foo
bar
quz

Por quê?

Em um forloop, a variável né definida uma vez e somente e, em seguida, essa definição é usada para todas as iterações. Portanto, cada bloco se refere ao mesmo nque possui um valor quzno momento em que o loop termina. Erro!

Em um eachloop, uma variável nova né definida para cada iteração, por exemplo, acima da variável né definida três vezes separadas. Portanto, cada bloco se refere a um separado ncom os valores corretos.


0

Até onde eu sei, usar blocos em vez de estruturas de controle no idioma é mais idiomático.


0

Eu só quero fazer um ponto específico sobre o loop for in em Ruby. Pode parecer uma construção semelhante a outras linguagens, mas na verdade é uma expressão como qualquer outra construção em loop no Ruby. De fato, o for in funciona com objetos Enumerable, assim como o iterador de cada.

A coleção passada para for in pode ser qualquer objeto que possua um método para cada iterador. Matrizes e hashes definem cada método, e muitos outros objetos Ruby também. O loop for / in chama cada método do objeto especificado. Como esse iterador gera valores, o loop for atribui cada valor (ou cada conjunto de valores) à variável (ou variáveis) especificada e, em seguida, executa o código no corpo.

Este é um exemplo bobo, mas ilustra o ponto em que o loop for trabalha com QUALQUER objeto que possua um método each, assim como o iterador de cada um:

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 agora o cada iterador:

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

Como você pode ver, ambos estão respondendo a cada método que gera valores de volta ao bloco. Como todos aqui declararam, é definitivamente preferível usar cada iterador no loop for. Eu só queria voltar para casa a ponto de não haver nada mágico no loop for in. É uma expressão que chama cada método de uma coleção e a passa para seu bloco de código. Portanto, é um caso muito raro que você precisaria usar para o in. Use o cada iterador quase sempre (com o benefício adicional do escopo do bloco).


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

No loop 'for', a variável local ainda permanece após cada loop. No loop 'each', a variável local é atualizada após cada loop.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.