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などを切り落として永続化する。
ちなみにマルチスレッドには対応していないので注意。
(そっと修正。)