Rubyのループについて簡単に質問しました。コレクションを反復処理するこれら2つの方法に違いはありますか?
# way 1
@collection.each do |item|
# do whatever
end
# way 2
for item in @collection
# do whatever
end
これらがまったく同じかどうか、あるいは多少微妙な違いがあるのか(おそらく@collection
がnilのとき)疑問に思うだけです。
これが唯一の違いです。
それぞれ:
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
メソッドの単なる構文糖衣です。
@collection
がnil
の場合、両方のループは例外をスローします。
例外:未定義のローカル変数またはmain:Objectに対するメソッド `@collection '
良い説明は " forループの悪 "を参照してください(変数スコープを考慮すると1つ小さな違いがあります)。
each
を使用することは より慣用的と見なされます Rubyを使用することです。
あなたの最初の例
@collection.each do |item|
# do whatever
end
もっと慣用句です 。 Rubyはfor
やwhile
のようなループ構造をサポートしていますが、ブロック構文が一般的には好まれます。
もう1つの微妙な違いは、for
ループ内で宣言した変数はすべてループ外で使用できるようになることです。一方、イテレータブロック内の変数は実質的に非公開です。
もう一つ違う。
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
違いがないように見えます、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つの理由です。
バグを引き起こす可能性がある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
を参照します。
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に使用する必要があることは非常にまれです。ブロック反復の利点を追加して、各反復子をほぼ常に使用します。
私の知る限りでは、言語内の制御構造の代わりにブロックを使用することはより慣用的です。
(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'ループでは、ローカル変数は各ループの後に更新されます。