【Ruby】【メタプログラミング】遅延評価のためのプロキシオブジェクト、作り方。
■topic summary
study about "lazy evaluation" using method_missing and call method.
Rubyベストプラクティス -プロフェッショナルによるコードとテクニック
- 作者: Gregory Brown,高橋征義,笹井崇司
- 出版社/メーカー: オライリージャパン
- 発売日: 2010/03/26
- メディア: 大型本
- 購入: 9人 クリック: 307回
- この商品を含むブログ (48件) を見る
遅延評価のためのプロキシオブジェクト
定義時ではなく、呼び出し時に評価するオブジェクトの作成方法。
フラグを用いた方法は以前実装したけど、今回は
・任意のブロックを実行する
・method_missingを利用して任意のメソッド呼び出しに対応する
が、異なる点。
module NaiveLazy class Promise < BasicObject def initialize(&b) @computation = b end def __result__ if @computation @result = @computation.call @computation = nil end @result end def method_missing(*a, &b) __result__.send(*a, &b) end end end class Cell def initialize(text) @text = text @width = NaiveLazy::Promise.new{ calculate_width } end attr_accessor :text, :width def calculate_width p "creating----- " + (@text * 10) @text * 10 end end cell = Cell.new("h") p "---- create cell ----" cell.width #<= inspectメソッドがmissing cell.width + "dayo" #<= +メソッドがmissing
■実行結果
"---- create cell ----" "creating----- hhhhhhhhhh"
calculate_widthは、定義時に実行されていないのが分かる。
inspectメソッドは独自に定義する
inspectメソッドを独自に定義することで、評価前/後か判断できる。
def inspect return "<NaiveLazy::Promise computation=#{@computation.inspect}" if @computation return @result.inspect end cell = Cell.new("h") p cell.width #<= inspectメソッドが呼び出される cell.width + "dayo" #<= @resultが生成される p cell.width #<= inspectメソッドが呼び出される
■実行結果
<NaiveLazy::Promise computation=#<Proc:0x26b34e8@C:/Users/_ADMIN/Desktop/test/arrays.rb:26> "hhhhhhhhhh"
inspect自体では、
newメソッドで渡したブロックの評価が行われないようになった。
全文
module NaiveLazy class Promise < BasicObject def initialize(&b) @computation = b end def __result__ if @computation @result = @computation.call @computation = nil end @result end def inspect return "<NaiveLazy::Promise computation=#{@computation.inspect}" if @computation return @result.inspect end def method_missing(*a, &b) __result__.send(*a, &b) end end end class Cell def initialize(text) @text = text @width = NaiveLazy::Promise.new{ calculate_width } end attr_accessor :text, :width def calculate_width @text * 10 end end cell = Cell.new("h") p cell.width #<= inspectメソッドが呼び出される cell.width + "dayo" #<= +メソッドがmissing p cell.width #<= inspectメソッドが呼び出される
ポイント
BasicObject(白紙のオブジェクト)を使う。
Objectを使うと、煩雑なメソッドを一個一個消していったのは前にやったところ
class TraceObject #TraceObjectの既存インスタンスメソッドを消す instance_methods.each do |m| next if m == :inspect || m == :object_id || m == :__id__ || m == :__send__ undef_method m end ・・・
ふむー。。