您如何对Ruby代码进行字符串化/序列化? [英] How do you stringize/serialize Ruby code?

查看:48
本文介绍了您如何对Ruby代码进行字符串化/序列化?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我希望能够在我的Ruby代码中编写一个lambda/Proc,对其进行序列化,以便可以将其写入磁盘,然后在以后执行该lambda.有点像...

I want to be able to write a lambda/Proc in my Ruby code, serialize it so that I can write it to disk, and then execute the lambda later. Sort of like...

x = 40
f = lambda { |y| x + y }
save_for_later(f)

稍后,在另一个Ruby解释器中,我想说...

Later, in a separate run of the Ruby interpreter, I want to be able to say...

f = load_from_before
z = f.call(2)
z.should == 42

Marshal.dump不适用于Procs.我知道Perl有数据:: Dump :: Streamer ,在Lisp中这很简单.但是有没有办法在Ruby中做到这一点?换句话说,save_ for _以后的实现是什么?

Marshal.dump does not work for Procs. I know Perl has Data::Dump::Streamer, and in Lisp this is trivial. But is there a way to do it in Ruby? In other words, what would be the implementation of save_for_later?

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

Edit: My answer below is nice, but it does not close over free variables (like x) and serialize them along with the lambda. So in my example ...

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

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

... the string output does not include a definition for x. Is there a solution that takes this into account, perhaps by serializing the symbol table? Can you access that in Ruby?

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

Edit 2: I updated my answer to incorporate serializing local variables. This seems acceptable.

推荐答案

使用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一起序列化.

This is great, but it does not close over free variables (like x) and serialize them along with the lambda.

序列化变量,您也可以遍历local_variables并对其进行序列化.但是问题是,save_for_later中的local_variables仅访问上述代码中的cs-即序列化代码局部的变量,而不是调用方.因此,不幸的是,我们必须将对局部变量及其值的获取推送给调用者.

To serialize variables also, you can iterate over local_variables and serialize them as well. The problem, though, is that local_variables from within save_for_later accesses only c and s in the code above -- i.e. variables local to the serialization code, not the caller. So unfortunately, we must push the grabbing of local variables and their values to the caller.

不过,这也许是一件好事,因为总的来说,在一段Ruby代码中查找所有自由变量是

Maybe this is a good thing, though, because in general, finding all free variables in a piece of Ruby code is undecidable. Plus, ideally we would also save global_variables and any loaded classes and their overridden methods. This seems impractical.

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

Using this simple approach, you get the following:

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

这篇关于您如何对Ruby代码进行字符串化/序列化?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆