Ruby のローカル変数の仕様が、さっぱり分らず、いろんな文献/ページや、実験をしてみてようやく分りました。
注:これは、Ruby ではどう書くべきかということではなく、仕様はどうなっているのかという話です。
スコープ
Ruby 書籍には、ローカル変数には以下のスコープがあると書かれています。
- メソッド
- クラス
- モジュール
- ブロック
しかし書籍には、プログラムのトップレベルに、いきなりローカル変数が現れます。
x = 1 # トップレベル puts x # => 1
よくよく調べてみると、トップレベルというスコープもあるそうです。だったら、はじめからこう書くべきではないですか?
Ruby の変数には、以下のスコープがあります。
- メソッド
- クラス
- モジュール
- ブロック
- トップレベル
ブロック
あるスコープからは、外側のスコープのローカル変数は見えません。なので、あるスコープで、あるローカル変数に代入すると、新しいローカル変数が定義されたことになります。
x = 1 # トップレベル def foo() x = 2 # メソッド end foo() puts x # => 1
Ruby の書籍には、begin/end にも do/end にもブロックという言葉が使われています。スコープで言うブロックとは、do/end です。
1.times do x = 0 # ブロックレベル end p x # トップレベルには x がないのでエラー
スコープの視点からすると、begin/end ではありません。
begin x = 0 # 単なるトップレベル end while false p x # => 0
さて、do/end のブロックからは、例外として外のスコープが見えます。
1.times do x = 0 # ブロックレベル 1.times do x = 1 # 外のブロックレベル end puts x # => 1 end
do/end ブロック内に、外のスコープにあるローカル変数名を使って、別の新たなローカル変数は定義できないようです。
感想
Ruby のローカル変数は分りにくいです。その要因は、以下のようなことでしょう。
- ちゃんとした説明を発見できない
- 宣言なしで変数を使う思想
- ブロックに例外がある
- スコープから見ると do/end がブロックで、begin/end はブロックではない
- do/end ブロックからは、外のスコープが見えるという例外がある
勘違いがあれば、指摘して下さい。(_ _)