当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《你如何串行化/序列化Ruby代码?》经验,为你挑选了2个好方法。

我希望能够在我的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:我更新了我的答案,以结合序列化局部变量.这似乎可以接受.



1> Jonathan Tra..:

使用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并序列化它们.但问题是,local_variables仅从save_for_later访问内部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



2> Jonathan Tra..:

使用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,并捕获本地,实例,类和全局变量.

推荐阅读
mobiledu2402851377
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有