さくらんぼのlambda日記

lambdaちっくなことからゲーム開発までいろいろ書きます。

スコープが楽しい

最近すっきりしないことが多いのでRubyの重箱の隅を延々と突っついているのですが
ちょっとスコープがおもしろいのでまとめてみます。



気持ち悪いのはブロックのスコープの周りなのでそのあたりについてまとめていきます。

ちなみにブロックというのは以下のeachの例のように do , endでくくっている
間の部分のことです。この例では1から10までを順番にbという変数に代入して
出力しています。

(1..10).to_a.each do |b|
  puts b
end

実行結果

1
2
3
4
5
6
7
8
9
10

さて問題にしたいのは次の例からです。

b = 1000
puts "b=#{b}"
(1..10).to_a.each do |b|
  puts b
end
puts "b=#{b}"

実行結果
b=1000
2
3
4
5
6
7
8
9
10
b= 10

なんと、このブロック文の中で宣言したはずのbは外側のbへ影響を及ぼしてしまいます。
iなどという変数を使っている場面があったら注意しないといけませんね。


次に、このブロックで宣言する変数は新しく変数宣言してくれるのかということを
調べてみます。

(1..10).to_a.each do |b|
  puts b
end
puts "b= #{b}"

これを実行すると、予想ではbに10が入っていると予想されますが

undefined local variable or method `b' for main:Object (NameError)

などと怒られてしまいます。


これはRubyのブロックの処理の仕方に起因しています。
Rubyの処理系はブロックを見つけると

  1. その宣言されている場所の環境(変数とその値の集合など)を保存。
  2. ブロックが評価されるときはまず先に保存した環境を参照しながら評価。
  3. さらにそのブロックのための新しい環境を作成。
  4. ブロックの評価が終了するとブロックのための環境は破棄される。

という流れで評価されます。
これを理解していないとハマる場面がありそうですね。

みなさんも気をつけましょう。