如何对Ruby代码进行字符串化/序列化?

内容来源于 Stack Overflow,并遵循CC BY-SA 3.0许可协议进行翻译与使用

  • 回答 (2)
  • 关注 (0)
  • 查看 (31)

我希望能够在我的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中做到这一点?

提问于
用户回答回答于

使用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

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

为了序列化变量,也可以迭代local_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

适用于Ruby1.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

扫码关注云+社区