メタプログラミングRuby 第3章 まとめ (ブロック)
第三章でのキーワード
- ブロック
- 束縛
- クロージャ
- proc
- lambda
- 遅延評価
ブロック
- ブロックを定義できるのはメソッドを呼び出すときだけ
- ブロックを受け取ったメソッドはyieldでブロックをコールバック(展開して利用)する
- ブロックとはメソッドに渡す無名引数みたいなもの
- ブロックもメソッドと同様に、最終行を評価した結果を返す
- block_given?メソッドで、ブロックが送られたかどうか確認できる
def test_method puts block_given? end test_method # => false test_method{"hogehoge"} # => true
- ブロック = コード + 束縛
束縛
- ブロックを定義したとき、その場所の束縛を取得する
- ブロックをメソッドに渡すときは、その束縛も一緒にもっていく
- 変数とかはブロックが定義されたときの値をみる(ブロックの引数は除く)
- この特性を「クロージャ」という
def test_method2 x = "x in Method" y = "y in Method" yield("yield argument") end x = "x out of Method" y = "y out of Method" test_method2{|y| puts x # => x out of Method puts y # => yield argument }
- ブロックの中で新しい束縛を作成することも出来、この中で宣言された変数はブロックが終了すると消えてしまう。
この変数をブロックローカル変数という
プログラムがスコープを切り替える場所
- クラス定義
- モジュール定義
- メソッド呼び出し
これらは、それぞれ、class, module, defといったキーワードではじまるので、それらはスコープゲート(新しいスコープのはじまる門・入り口)と呼ばれる
(余談だがこの辺はRubyのゴールドの試験によく出てきた気がする。このコードを実行すると変数にはどの値が入ってるかみたいな)
クラスやモジュールの定義のコードはすぐに実行されるが、メソッド定義のコードはメソッドを呼び出したときにはじめて実行される
class TestClass puts "Enter class!!" # => 実行される def test_method puts "Enter method!!" # => 実行されない end end module TestModule puts "Enter module!!" # 実行される end
スコープゲート(class, def)をメソッド(class.new(), define_method())に置き換えると、他のスコープの変数を参照できるようになる
この技術をレキシカルスコープ、フラットスコープ、また単にスコープのフラット化という
Proc, lambda
ブロックの使用は
- コードを保管する
- 保管されたブロックを呼び出して実行する(yield)
この保管して呼び出すという方法は以下のものがある
- ブロック
- Proc = ブロックがオブジェクトになったmの
- lambda = Procの変形
- メソッド
Rubyではほぼ全てがオブジェクトだが、ブロックは違う。だから保存しておいて後で使うということが出来ない
=> Procを使えばいい
Proc
プロックにブロックを渡すことでオブジェクト化できる
オブジェクト = Proc.new{#ブロック}
Procを呼び出すのはProc.call
inc = Proc.new{|x| x -1} ###色々な処理 inc.call(2)
この技術を「遅延評価」という
ブロックをProcに変換する方法
- proc()
- lambda()
ブロックではなく、Procを使う場面
- ブロックを受け取って、さらに他のメソッドに渡したいとき
- ブロックをProcにして保管しておきたいとき
Procを ブロックとして受け取る
最後の仮引数の頭に&をつければブロックとして受け取るという意味になる
def teach_math(a, b, &operation) puts math(a, b, &operation) end def math(a, b) yield(a, b) end teach_math(2, 3){|x, y| x * y } # => 6
Procをブロックにして送る
送るときに&をつける
def my_method(greeting) puts "#{greeting} #{yield}" end my_proc = proc{"Bill"} my_method("Hello", &my_proc) # => Hello Bill