如何在Ruby中编组lambda(Proc)?

时间:2008-08-23 04:22:45

标签: ruby serialization lambda proc-object

Joe Van Dyk asked the Ruby mailing list

  

您好,

     

在Ruby中,我猜你不能编组一个lambda / proc对象,对吧?是   在lisp或其他语言中可能的那种?

     

我想做的事情:

l = lamda { ... }
Bj.submit "/path/to/ruby/program", :stdin => Marshal.dump(l)
  

所以,我正在向BackgroundJob发送一个lambda对象,其中包含   用于做什么的上下文/代码。但是,猜测这是不可能的。一世   结束了编组包含指令的普通红宝石对象   程序运行后该怎么做。

     

7 个答案:

答案 0 :(得分:21)

你不能编组Lambda或Proc。这是因为它们都被认为是闭包,这意味着它们靠近定义它们的内存并且可以引用它。 (为了编组它们,你必须将它们在创建时可以访问的所有内存编组。)

正如Gaius指出的那样,你可以使用ruby2ruby来获取该程序的字符串。也就是说,您可以封送代表ruby代码的字符串,然后再重新评估它。

答案 1 :(得分:11)

你也可以输入你的代码作为字符串:

code = %{
    lambda {"hello ruby code".split(" ").each{|e| puts e + "!"}}
}

然后用eval执行它

eval code

将返回ruby lamda。

使用%{}格式转义字符串,但仅在不匹配的大括号上关闭。即你可以像这样%{ [] {} }嵌套大括号,它仍然是封闭的。

大多数文本语法荧光笔没有意识到这是一个字符串,所以仍然会显示常规代码突出显示。

答案 2 :(得分:4)

如果您对使用Ruby2Ruby获取Ruby代码的字符串版本感兴趣,可能需要this thread

答案 3 :(得分:3)

尝试ruby2ruby

答案 4 :(得分:1)

我发现proc_to_ast做得最好:https://github.com/joker1007/proc_to_ast

在ruby 2+中肯定可以使用,我已经为ruby 1.9.3+兼容性创建了一个PR(https://github.com/joker1007/proc_to_ast/pull/3

答案 5 :(得分:0)

如果将proc定义到文件中,U可以获取proc的文件位置然后序列化它,然后在反序列化之后使用该位置再次返回到proc

  

proc_location_array = proc.source_location

反序列化后

  

file_name = proc_location_array [0]

     

line_number = proc_location_array [1]

     

proc_line_code = IO.readlines(file_name)[line_number - 1]

     

proc_hash_string = proc_line_code [proc_line_code.index(" {").. proc_line_code.length]

     

proc = eval(" lambda#{proc_hash_string}")

答案 6 :(得分:0)

从前,使用宝石内部宝石(https://github.com/cout/ruby-internal)可以做到这一点,例如:

p = proc { 1 + 1 }    #=> #<Proc>
s = Marshal.dump(p)   #=> #<String>
u = Marshal.load(s)   #=> #<UnboundProc>
p2 = u.bind(binding)  #=> #<Proc>
p2.call()             #=> 2

有一些警告,但是已经有很多年了,我不记得细节了。例如,我不确定如果变量在转储的绑定中是dynvar,而在绑定的本地是重新绑定的本地变量,会发生什么情况。对AST(在MRI上)或字节码(在YARV上)进行序列化并非易事。

以上代码适用于YARV(最高1.9.3)和MRI(最高1.8.7)。没有理由不花一点精力就无法使其在Ruby 2.x上运行。