web-dev-qa-db-ja.com

Rubyにおける "for" vs "each"

Rubyのループについて簡単に質問しました。コレクションを反復処理するこれら2つの方法に違いはありますか?

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

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

これらがまったく同じかどうか、あるいは多少微妙な違いがあるのか​​(おそらく@collectionがnilのとき)疑問に思うだけです。

188
mportiz08

これが唯一の違いです。

それぞれ:

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

のために

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

forループでは、イテレータ変数はブロックが終了した後も存続します。 eachループでは、ループの開始前にローカル変数として定義されていない限り、実行されません。

それ以外のforは、eachメソッドの単なる構文糖衣です。

@collectionnilの場合、両方のループは例外をスローします。

例外:未定義のローカル変数またはmain:Objectに対するメソッド `@collection '

299
Jeremy Ruten

良い説明は " forループの悪 "を参照してください(変数スコープを考慮すると1つ小さな違いがあります)。

eachを使用することは より慣用的と見なされます Rubyを使用することです。

43
ChristopheD

あなたの最初の例

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

もっと慣用句です 。 Rubyはforwhileのようなループ構造をサポートしていますが、ブロック構文が一般的には好まれます。

もう1つの微妙な違いは、forループ内で宣言した変数はすべてループ外で使用できるようになることです。一方、イテレータブロック内の変数は実質的に非公開です。

27
Bayard Randel

もう一つ違う。

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 

ソース: http://paulphilippov.com/articles/enumerable-each-vs-for-loops-in-Ruby

より明確にするために: http://www.Ruby-forum.com/topic/179264#784884

6
Mr. Black

違いがないように見えます、forはその下にeachを使用します。

$ 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

ベヤードが言うように、それぞれはもっと慣用的です。それはあなたからより多くを隠し、特別な言語機能を必要としません。 Telemachusのコメントによる

for .. in ..は、ループの範囲外でイテレータを設定します。

for a in [1,2]
  puts a
end

ループ終了後にaが定義されたままになります。 eachはそうではありません。 temp変数の寿命が短いため、これがeachを使用するもう1つの理由です。

2
BaroqueBobcat

バグを引き起こす可能性があるforを決して使用しないでください。

違いは微妙ですが、多大なバグを引き起こす可能性があります。

だまされてはいけません、これは慣用的なコードやスタイルの問題ではありません。これはプロダクションコードのなかでほとんど追跡不可能なバグを避けるための問題です。 Rubyによるforの実装には重大な欠陥があるため、使用しないでください。常にeachループを使用し、決してforループを使用しないでください。

これはforがバグを引き起こす例です。

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

版画

quz
quz
quz

%w{foo bar quz}.each { |n| ... }プリントを使う

foo
bar
quz

どうして?

forループでは、変数nが一度だけ定義され、その定義がすべての反復に使用されます。したがって、ループが終了するまでに、各ブロックは同じnを参照しています。この変数の値はquzです。バグ!

eachループでは、反復ごとに新しい変数nが定義されています。たとえば、変数nは3回に分けて定義されています。したがって、各ブロックは正しい値を持つ別々のnを参照します。

1
akuhn

Rubyのfor in loopについて具体的に述べたいと思います。他の言語に似た構文のように思えるかもしれませんが、実際にはRubyの他のすべてのループ構造のような表現です。実際、for inはEnumerableオブジェクトを各イテレータと同じように機能します。

Inに渡されるコレクションは、各イテレータメソッドを持つ任意のオブジェクトです。配列とハッシュはそれぞれのメソッドを定義し、他の多くのRubyオブジェクトも同様に定義します。 for/inループは、指定されたオブジェクトの各メソッドを呼び出します。その反復子が値を生成すると、forループは各値(または各値のセット)を指定された変数(または複数の変数)に割り当ててから、本体内でコードを実行します。

これは愚かな例ですが、forループが各メソッドを持つANYオブジェクトと連携するという点を示しています。これは、各イテレータの動作と同じです。

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

そして今、それぞれのイテレータ:

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

お分かりのように、両方ともブロックに値を返すそれぞれのメソッドに応答しています。ここでみんなが述べたように、forループの上で各イテレータを使うことが絶対に望ましいです。 for in loopについて魔法のようなものは何もないという点を、家に押し戻したかっただけです。これは、コレクションの各メソッドを呼び出してそれをコードブロックに渡す式です。したがって、inに使用する必要があることは非常にまれです。ブロック反復の利点を追加して、各反復子をほぼ常に使用します。

0
Donato

私の知る限りでは、言語内の制御構造の代わりにブロックを使用することはより慣用的です。

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

'for'ループでは、ローカル変数は各ループの後でまだ生きています。 'each'ループでは、ローカル変数は各ループの後に更新されます。

0
Kelvin Tan