遅延評価

世の中遅延評価が流行っているみたいです。
僕も抽象構文木を作って遅延評価する逆ポーランド電卓を作ってみました。値が再利用されないので、ぜんぜん面白くありません。いまいち。やっぱり関数定義くらいはできないとなぁ。

class Node
  def initialize(name, func, *args)
    @name, @func, @args = name, func, args
    @value = @func ? nil : args.first
  end

  def evaluate
    unless @value
      puts "evaludate >>> #{self.inspect}"
      @value = @func[*@args]
      @func = nil
      @args = nil
      puts "evaludate <<< #{self.inspect}"
    end
    @value
  end

  def inspect
    if @value
      @value.inspect
    else
      "(#{([@name] + @args).map {|arg| arg.inspect }.join(', ')})"
    end
  end
end

def rpcalc(s)
  tree = []
  s.split(/\s+/).each do |token|
    tree = case token
           when '+'
             [Node.new(:+, lambda {|a, b| a.evaluate + b.evaluate}, *tree)]
           when '-'
             [Node.new(:-, lambda {|a, b| a.evaluate - b.evaluate}, *tree)]
           when '*'
             [Node.new(:*, lambda {|a, b| a.evaluate * b.evaluate}, *tree)]
           when '/'
             [Node.new(:/, lambda {|a, b| a.evaluate / b.evaluate}, *tree)]
           else
             tree + [Node.new(nil, nil, token.to_i)]
           end
  end
  tree.first.evaluate
end

p rpcalc('1 2 + 4 * 2 /')
# => evaludate >>> (:/, (:*, (:+, 1, 2), 4), 2)
#    evaludate >>> (:*, (:+, 1, 2), 4)
#    evaludate >>> (:+, 1, 2)
#    evaludate <<< 3
#    evaludate <<< 12
#    evaludate <<< 6
#    6