Rubyの「for」と「each」


200

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

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

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

これらがまったく同じかどうか、または微妙な違いがあるかどうか(おそらく@collectionがnilの場合)を考えています。

回答:


315

これが唯一の違いです。

各:

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 '


3
xがforケースに残っている、またはこの悪いデザインは:Pであるという正当な理由がありますか?これは他のほとんどの言語と比較して直感的ではありません。
cyc115

3
@ cyc115 forシナリオがx残る理由は、(一般的に言えば)キーワードでは新しいスコープが作成されないためです。ifunlessbeginforwhileなどはすべて、現在のスコープで機能します。ただし、ブロックを受け入れます。ブロックは常に現在のスコープの上に独自のスコープを追加します。ブロックで新しい変数を宣言する(つまり、新しいスコープ)ことは、その追加のスコープが利用できないため、ブロックの外部からアクセスできないことを意味します。#each
3limin4t0r

43

わかりやすい説明については、「Forループの弊害」を参照してください(変数のスコープを考慮すると、1つの小さな違いがあります)。

使い方がeachされ、より慣用的と考えルビーを使用します。


@zachlatta:通知ありがとうございます。リンクを編集して、記事のwebarchive.orgバリアントを指すようにします!
ChristopheD

1
graysoftinc.com/early-steps/the-evils-of-the-for-loopが新しいリンクになり、JEG2のサイトがオンラインに戻りました。
pnomolos 2014年

30

あなたの最初の例、

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

より慣用的です。Rubyはforおよびのようなループ構造をサポートしていwhileますが、一般的にはブロック構文が推奨されます。

もう1つの微妙な違いは、forループ内で宣言した変数はループの外でも使用できるのに対し、反復子ブロック内の変数は実質的にプライベートであることです。


whileそしてuntil、実際にそのような例一意の値を生成するか、REPLのためのために、各に置き換えることができないいくつかの非常に具体的な用途があります。
最大

6

もう1つ…

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


2

違いはないようですが、下で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ないところ。これはeach、temp変数の有効期間が短いため、を使用する理由の1つです。


1
変数のスコープに関して、小さな違いがあります(yjerem、ChristopheD、Bayardによる言及)。
Telemachus 2010

不正解、下でforは使用しませんeach。他の回答を参照してください。
akuhn 2016

@akuhn詳細については、この質問とその優れた回答の両方を参照してください。
Sagar Pandya 2016

2

絶対に使用forしないでください。ほとんど追跡できないバグが発生する可能性があります。

だまされてはいけません、これは慣用的なコードやスタイルの問題についてではありません。Rubyのの実装に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反復ごとに定義され、変数上記例えばn3つの別々の回に定義されています。したがって、各ブロックはn正しい値で個別を参照します。



0

Rubyのforループについて特定の点を述べたいだけです。それは他の言語に似た構成要素のように見えるかもしれませんが、実際には、Rubyの他のすべてのループ構成要素と同様の式です。実際、for inは、各イテレータと同じようにEnumerableオブジェクトを処理します。

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

これはばかげた例ですが、forループが、各反復子が行うのと同じように、eachメソッドを持つ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

ご覧のように、どちらもブロックに値を返すeachメソッドに応答しています。ここで述べたように、forループではなく、各反復子を使用することが明らかに望ましいです。私はfor inループに魔法のようなものは何もないという点に帰りたかっただけです。これは、コレクションの各メソッドを呼び出し、それをコードのブロックに渡す式です。したがって、これを使用する必要があるのは非常にまれなケースです。各反復子をほぼ常に使用します(ブロックスコープの利点が追加されます)。


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」ループでは、ローカル変数は各ループの後でまだ有効です。「各」ループでは、ローカル変数は各ループの後に更新されます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.