你如何串行化/序列化Ruby代码?

我希望能够在我的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实现什么?

编辑 : 我的答案很好,但它不会关闭自由变量(如x )并将它们与lambda序列化。 所以在我的例子中……

 x = 40 s = save_for_later { |y| x + y } # => "lambda { |y|\n (x + y)\n}" 

…字符串输出不包含x的定义。 有没有一个解决方案考虑到这一点,也许是通过序列化符号表? 你可以在Ruby中访问它吗?

编辑2 :我更新了我的答案,以结合序列化局部变量。 这似乎可以接受。

使用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) # => # g.call(2) # => 42 

这很好,但它不会关闭自由变量(如x )并将它们与lambda一起序列化。

要同时序列化变量 ,您还可以迭代local_variables并将它们序列化。 但问题是, save_for_later中的local_variables只访问上面代码中的cs – 即序列化代码本地的变量,而不是调用者。 所以不幸的是,我们必须将局部变量及其值的抓取推送给调用者。

也许这是一件好事,因为一般来说,在一段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}") # => # g.call(2) # => 42 # Changing x does not affect it. x = 7 g.call(3) # => 43 

使用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) # => # g.call(2) # => 42 

请参阅我关于捕获自由变量的其他答案 。

更新 :现在您还可以使用serializable_proc gem,它使用sourcify,并捕获本地,实例,类和全局变量。

看看这个问题的答案。

Ruby有Marshal类,它有一个可以调用的转储方法。

看看这里:

http://rubylearning.com/satishtalim/object_serialization.html