使用 Ruby2Ruby
def save_for_later(&block)
return nil unless block_given?
c = Class.new
c.class_eval do
define_method :serializable, &block
end
s = Ruby2Ruby.translate(c, :serializable)
s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1|').sub(/end$/, '}')
end
x = 40
s = save_for_later { |y| x + y }
# => "lambda { |y|\n (x + y)\n}"
g = eval(s)
# => #<Proc:0x4037bb2c@(eval):1>
g.call(2)
# => 42
这很棒,但它不会关闭自由变量(如x
)并将它们与 lambda 一起序列化。
要序列化变量,您也可以迭代local_variables
并序列化它们。但是,问题在于仅在local_variables
内部save_for_later
访问c
和s
在上面的代码中——即序列化代码的本地变量,而不是调用者。所以不幸的是,我们必须将局部变量及其值的获取推送给调用者。
不过,这也许是件好事,因为一般来说,在一段 Ruby 代码中找到所有自由变量是不可判定的。另外,理想情况下,我们还将保存global_variables
任何已加载的类及其覆盖的方法。这似乎不切实际。
使用这种简单的方法,您将获得以下信息:
def save_for_later(local_vars, &block)
return nil unless block_given?
c = Class.new
c.class_eval do
define_method :serializable, &block
end
s = Ruby2Ruby.translate(c, :serializable)
locals = local_vars.map { |var,val| "#{var} = #{val.inspect}; " }.join
s.sub(/^def \S+\(([^\)]*)\)/, 'lambda { |\1| ' + locals).sub(/end$/, '}')
end
x = 40
s = save_for_later(local_variables.map{ |v| [v,eval(v)] }) { |y| x + y }
# => "lambda { |y| _ = 40; x = 40;\n (x + y)\n}"
# In a separate run of Ruby, where x is not defined...
g = eval("lambda { |y| _ = 40; x = 40;\n (x + y)\n}")
# => #<Proc:0xb7cfe9c0@(eval):1>
g.call(2)
# => 42
# Changing x does not affect it.
x = 7
g.call(3)
# => 43