[Ruby] Ruby の for と each の違いは変数のスコープ

ハマったのでメモ。 for と each の違いです。

t = []
for i in [1,2,3]
# [1,2,3].each do |i|
  t << Thread.new do
    puts "start #{i}"
    sleep i
    p Thread.current
    puts "end #{i}"
  end
end

p t

while t[0].alive?
end

t[1].kill
t[2].kill

p t

こいつに thread.rb とか名前つけて、実行

$ ruby thread.rb
start 1
start 2
start 3
[#<Thread:0x100169be8 sleep>, #<Thread:0x100169ad0 sleep>, #<Thread:0x1001699b8 sleep>]
#<Thread:0x100169be8 run>
end 3
[#<Thread:0x100169be8 dead>, #<Thread:0x100169ad0 dead>, #<Thread:0x1001699b8 dead>]

アレ?ちゃんと t[0] が終わってるんだし、そこは end 1 って出てほしいんだけど。
そこで、 for のところを each に変えて実行。

$ ruby thread.rb
start 1
start 2
start 3
[#<Thread:0x100169b20 sleep>, #<Thread:0x1001699b8 sleep>, #<Thread:0x100169850 sleep>]
#<Thread:0x100169b20 run>
end 1
[#<Thread:0x100169b20 dead>, #<Thread:0x1001699b8 dead>, #<Thread:0x100169850 dead>]

こんどはちゃんと end 1 が出る。
あれーと悩んでいたら、次のページを発見。

for =>そのfor文が定義されているスコープに対して、iを定義する
each=>eachブロック内でのみ生存する新たなスコープを用意し、そこでiを定義する

http://ipcom.withgood.net/wordpress/?p=484

つまり、この場合は i がグローバルになっちゃうので、 for のループごとに i の値が書き変わっちゃうのが原因でした。
3つめのスレッドを作った時点で i の値は 3 になり、1つめのスレッドが終了したときその i の値を参照するので 3 が出力されると。
Ruby では基本的に for ではなく each を使うのがよさそうですね。。