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

发布于 2024-07-07 08:57:35 字数 1034 浏览 10 评论 0原文

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

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)

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 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?

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}"

... 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?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

无远思近则忧 2024-07-14 08:57:35

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

序列化变量,您可以迭代 < code>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

Use 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

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

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.

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
美人如玉 2024-07-14 08:57:35

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

请参阅我的其他答案以捕获自由变量。

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

Use sourcify

This will work on Ruby 1.8 or 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

See my other answer for capturing free variables.

Update:
Now you can also use the serializable_proc gem, which uses sourcify, and captures local, instance, class, and global variables.

尸血腥色 2024-07-14 08:57:35

查看此问题的答案。

Check out the answers to this question.

酒几许 2024-07-14 08:57:35

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

看一下这里:

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

Ruby has the Marshal class that has a dump method that you can call.

Take a look here:

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文