実装して理解する遅延評価の仕組み 〜 thunkを絵に描いて理解しよう・JavaScriptでHaskellを実装!?をRubyで写経してみます。
- Thunk、 Lambda、App、Evaluateクラスは instance_of? により型を調べるためのものです。
- Thunk でくるんである以外は lambda から取り出すだけ。
class Thunk attr_accessor :value def initialize(value) @value = value end end class Lambda attr_accessor :fn def initialize(fn) @fn = fn end end class App attr_accessor :fn, :val def initialize(f, v) @fn = f @val = v end end # apply はAppオブジェクトを作ります。 # apply(fn).call(val) : apply は関数を引数にとり、val を引数としてcallされます。 def apply (fn) lambda{|val| Thunk.new( lambda{ App.new(fn, val)} ) } end class Evaluate attr_accessor :fn, :val def initialize(fn, val) @fn = fn @val = val end end def evaluate(val) while val.instance_of?(Thunk) val = val.value.call if val.instance_of?(App) val = peelLambda(evaluate(val.fn)).call(val.val) end end val end def peelLambda (lam) unless lam.instance_of?(Lambda) raise "type error: apply a non-lambda to a value" end lam.fn end
y = Thunk.new(lambda{20}) x = Thunk.new(lambda{y}) p evaluate(x) # => 20 add = Lambda.new(lambda{|x| Lambda.new(lambda{|y| Thunk.new(lambda{ evaluate(x) + evaluate(y)})})} ) sub = Lambda.new(lambda{|x| Lambda.new(lambda{|y| Thunk.new(lambda{ evaluate(x) - evaluate(y)})})} ) one = Thunk.new(lambda{1}) two = Thunk.new(lambda{2}) onetwo = Thunk.new(lambda{ apply( apply(add).call(one)).call(two) }) twoone = Thunk.new(lambda{ apply( apply(sub).call(one)).call(two) }) p evaluate(onetwo) # => 3 p evaluate(twoone) # => -1
class Cons attr_accessor :fn, :val def initialize(car, cdr) @car = car @cdr = cdr end end class Nil attr_reader :nil def initialize() @nil end end # [] nil_ = Thunk.new(lambda{Nil.new()}) # (:) cons = Lambda.new(lambda{|x| Lambda.new( lambda{|xs| Thunk.new( lambda{ Cons.new(x, xs)}) }) }) zero = Thunk.new(lambda{ 0 }) # let x = 0 : x x = Thunk.new(lambda{ Cons.new(zero, x) }) print evaluate(x) # => #<Cons:0x59ebd6c>