首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >如何串行化/序列化Ruby代码?

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

Stack Overflow用户
提问于 2008-10-14 00:44:42
回答 4查看 7.3K关注 0票数 16

我希望能够在Ruby代码中编写一个lambda/Proc,序列化它,这样我就可以将它写到磁盘上,然后再执行lambda。有点像..。

代码语言:javascript
复制
x = 40
f = lambda { |y| x + y }
save_for_later(f)

稍后,在Ruby解释器的单独运行中,我希望能够说...

代码语言:javascript
复制
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很好,但它不会关闭自由变量(如x)并将它们与一起序列化。所以在我的例子中。

代码语言:javascript
复制
x = 40
s = save_for_later { |y| x + y }
# => "lambda { |y|\n  (x + y)\n}"

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

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

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2008-10-14 02:16:44

使用Ruby2Ruby

代码语言:javascript
复制
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并序列化它们。然而,问题在于save_for_later中的local_variables只访问上述代码中的cs --即序列化代码的本地变量,而不是调用者。因此,不幸的是,我们必须将局部变量及其值的获取推送给调用者。

也许这是一件好事,因为一般来说,在一段Ruby代码中找到所有自由变量都是undecidable。另外,理想情况下,我们还会保存global_variables和所有加载的类以及它们被覆盖的方法。这似乎不切实际。

使用这种简单的方法,您将获得以下内容:

代码语言:javascript
复制
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
票数 12
EN

Stack Overflow用户

发布于 2011-05-20 21:30:15

使用

这将适用于Ruby 1.8或1.9。

代码语言:javascript
复制
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

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

票数 11
EN

Stack Overflow用户

发布于 2008-10-14 01:40:32

请查看this question的答案。

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/199603

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档