如何对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_实现是什么?

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

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

字符串输出不包括用于CCD_ 4的定义。有没有一个解决方案可以考虑到这一点,也许可以通过序列化符号表?你能用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)
# => #<Proc:0x4037bb2c@(eval):1>
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}")
# => #<Proc:0xb7cfe9c0@(eval):1>
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)
# => #<Proc:0x00000100e88450@(eval):1>
g.call(2) 
# => 42

请参阅我关于捕获自由变量的另一个答案。

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

查看此问题的答案。

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

看看这里:

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

最新更新