変数のデータがコードの中をどうやって動いているのかを考える
変数を検索すると、スコープと共に語られることが多い。実際、スコープは変数の有効範囲という認識で正しいはずなのだが、スコープを作り出すのは変数では無い。ある変数は、あるスコープを所持しているというわけではなく、ある変数は、あるスコープまででデータを参照できるということにすぎない。
つまり、変数がスコープを作っているのでは無く、変数は与えられたスコープという空間を生きることしかできない、ということ。では、何がスコープを作っているのかというと、それがブロックという概念になる。
ブロックとは
クロージャ、レキシカルスコープとも呼ばれる概念。クロージャは、メソッドの実行環境に対して変数が存在しない時に、その親スコープへと変数を参照する能力を持つ。
Rubyのブロックは,メソッドに与えるもの(メソッド呼び出し時に与えるもの)というところが本質です。
〈メソッドに与えるもの〉というと,引数(より詳しく言えば実引数)scivolaさんからのコメントより引用
引数を与えるために、参照先を探しに行く、と言える。
ブロックパラメータ
イテレータなどで||このようにパイプで括られたもの。ブロック変数とも呼ばれていることもある。語っている人によって同一概念を別単語で語られることが多いので、混乱する。
変数には何が内包され得るかの視点
cnt = ARGV[0].to_i
def loop(n)
(1..n).each do |n|
p "Hello World!! #{n}"
end
end
loop(cnt)
nだらけです。一応、このプログラムは動きますが、勉強を始めた人には?となるであろうコードです。というか私はなりました。なんと、ここに書かれたnという変数は二種類あってそれぞれが違うということです
このnが二種類ある理由を明確にするために、一種類の変数nを別の変数名に変えてみますn
cnt = ARGV[0].to_i
def loop(n)
(1..n),each do |i|
p "Hello World!! #{i}"
end
end
loop(ctn)
違いが見えてくると思います。
loopメソッドの引数nは、ARGVによってプロンプトから受け取る整数値。そして、この固定された整数値は、範囲オブジェクトにも使われています。iは、ループされた1からnまでの数値がループするたびに代入されるはずですので、pメソッドに番号を振りたい場合、nではなく、iを書かなければならないことがわかると思います。
しかし、これだと先ほど私が書いたブロックの概念がわかりにくいので、少しコードを変更します。
cnt = ARGV[0].to_i
def loop(n)
(1..n),each do |i|
p "Hello World!! #{n}"
end
end
これだと標準出力される数字は、変数nに代入された整数値ですが、eachで作られているブロック内で使われているnはこのブロック内でのどこにも変数として定義されていないのに、エラーを起こさないというところが重要です。
なぜ、エラーが起こってないかというと、pメソッドで変数展開されているnは、do~endで囲まれたブロックから、def~endで囲まれたブロック、つまり、ネストしている親ブロックに変数を取りにいっています。これが、クロージャ、ブロックの作るスコープです。
変数は様々に存在しますが、それを説明する時に、スコープを変数の有効範囲と定義してしまうと、あたかも変数がスコープを持っているような印象を受けます。が、正しくは、ブロックによって作られたスコープを越えていく範囲が変数によって決まっているということなのです。
変数の中身と、それらがどいうやって動いているのかを理解するのに非常に重要な視点だと思っています。普通の人は、これやらなくても理解できるようなのですが、私はここまで落とし込んでようやく腑に落ちました。
ちなみに、eachやtimesといったイテレータのようなブロックを作り出すメソッドをブロック付きメソッドと読んだりします。
補足
コメントをいただきましたが、ブロックだけが変数の動きを固めるわけではないです。同一インスタンス内で参照できるインスタンス変数などが良い例かと思います。今回は、ブロックについて考察した結果をまとめたものですので、ご了承ください。
補足2
ブロックを作るdo endは、{}のかっこで作ることもできる。