Closure

http://d.hatena.ne.jp/kilrey/20070705
の続き。
Rubyの標準の関数オブジェクトであるProcはMarshalやYAMLによる永続化が出来ない。
原因は評価環境であるBindingが永続化出来ないため、それを参照するProcも永続化出来ないということらしい。
よく考えたらProcもBlockへの参照を持つから、Bindingが永続化出来てもProcが永続化出来るとはならない。
それではということで環境をHashに変換することでClosureを作ってみた。

class Closure < Function
  @@passing_value = nil
  def initialize(env, src)
    super(src)
    @env = env#Hash相当のオブジェクト。言語仕様によっては連鎖型。
    @binding = nil
    @args = nil
    @star = nil
  end
  def to_proc()
    if (!defined?(@proc)) then
      #引数を切り出す。
      if (@src.gsub(/\s+/,"") =~ /^\|((?:\w+,?)*)(?:\*(\w+))?\|/) then
        @args = $1.split(",")
        @star = $2
      end
      #@envを環境に定義する。
      @binding = binding()
      for key in @env.keys()
        eval("#{key}=@env[\"#{key}\"]", @binding)
      end
      @proc = eval("proc{"+@src+"}", @binding)
    end
    return @proc
  end
  def call(*args)
    self.to_proc()#@bindingを生成する。
    for key in @env.keys()
      eval("#{key}=@env[\"#{key}\"]", @binding)
    end
    @args.each_with_index() do |arg, index|
      @@passing_value = args[index]
      eval("#{arg}=@@passing_value", @binding)
    end
    if (@star) then
      @@passing_value = args[@args.size(),-1]
      eval("#{@star}=@@passing_value", @binding)
    end
    ret = super(*args)
    for key in @env.keys()
      @env[key] = eval("#{key}", @binding)
    end
    return ret
  end
end

これくらいで概要は判ると思う。
要するにevalを使って@envの内容をローカル環境にコピーして実行する、
実行後にはローカル環境の内容を@envに書き出す、ということ。
とくに書いていないが、Functionと同様に@bindingなどを切り落として永続化する。
ちなみにマルチスレッドには対応していないので注意。
(そっと修正。)