我希望能够在我的Ruby代码中编写lambda / proc,对其进行序列化以便将其写入磁盘,然后再执行lambda。有点像......
x = 40
f = lambda { |y| x + y }
save_for_later(f)
稍后,在Ruby解释器的单独运行中,我希望能够说...
f = load_from_before
z = f.call(2)
z.should == 42
Marshal.dump不适用于Procs。我知道Perl有Data::Dump::Streamer,而在Lisp中这是微不足道的。但有没有办法在Ruby中做到这一点?换句话说,save
的实现是什么?_
for_
later
编辑:My answer below很不错,但它不会关闭自由变量(例如_
)并将它们与lambda一起序列化。所以在我的例子中......
_
...字符串输出不包含x
的定义。是否有一个解决方案考虑到这一点,可能是通过序列化符号表?你可以在Ruby中访问它吗?
编辑2 :我更新了我的答案,以合并序列化局部变量。这似乎是可以接受的。
答案 0 :(得分:11)
使用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一起序列化。
还要serialize variables,您可以迭代local_variables
并对它们进行序列化。但问题是local_variables
内的save_for_later
仅访问上述代码中的c
和s
- 即序列化代码本地的变量,而不是调用者。不幸的是,我们必须将局部变量及其值的抓取推送给调用者。
也许这是一件好事,因为一般来说,在一段Ruby代码中查找所有自由变量都是undecidable。另外,理想情况下,我们还会保存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
答案 1 :(得分:11)
使用sourcify
这适用于Ruby 1.8或1.9。
def save_for_later(&block)
block.to_source
end
x = 40
s = save_for_later {|y| x + y }
# => "proc { |y| (x + y) }"
g = eval(s)
# => #<Proc:0x00000100e88450@(eval):1>
g.call(2)
# => 42
请参阅my other answer以获取自由变量。
<强>更新强>: 现在,您还可以使用serializable_proc gem,它使用sourcify,并捕获本地,实例,类和全局变量。
答案 2 :(得分:2)
查看this question的答案。
答案 3 :(得分:-8)