読者です 読者をやめる 読者になる 読者になる

メタプログラミングRuby その6 [学習メモ]

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

水曜日: ブロック

ruby おなじみのブロックはコードと束縛の固まりで、{ } や do ~ end で定義される。
each 族でよく使われるあれだ。

4.3 ブロックはクロージャ

ブロックには束縛が含まれる。つまり

def test
  name = "Alice"
  yeild
end

name = "Bob"
test { |name| "hello, #{name}!" }
# => "hello, Bob!"

のようになる。
ここでは test メソッドに渡されたブロックは name にローカルで定義された値を束縛しており、メソッド中の name が使われることはない。

4.3.3 スコープのフラット化

一般的に言って ruby のスコープは狭く、メソッドやクラスの外側の束縛にアクセスするのは結構面倒くさい。
もちろんそのおかげでコードが安全になるし、リファクタリングもしやすくなる訳なのだけれど、どうしてもスコープ外の束縛を使いたいときもある。
パッと思いつくのは引数として渡すと言うことだが、あまりにも不格好すぎる。
そこでメソッドでクラスやメソッドを定義することで、この問題をクリアできる。

out_var = "success"

Test = Class.new do
  puts "#{out_var} in the class!"

  define_method :test_method do
    "#{out_var} in the method!"
  end
end

複数のメソッドで変数を共有して、なおかつそれ以外からのアクセスは防ぎたい場合このやり方が使える。

def define_counter
  shared = 0

  Kernel.send :define_method, :counter do
    shared
  end

  Kernel.send :define_method, :inc do |x|
    shared += x
  end
end

define_counter

counter
# => 0
inc(4)
counter
# => 4

おなじみのクロージャ利用法だ。