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

Joe Van Dyk 询问了Ruby邮件列表 :

嗨,

在Ruby中,我猜你不能编组一个lambda / proc对象,对吗? 在lisp或其他语言中这可能吗?

我想做什么:

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

所以,我向BackgroundJob发送一个lambda对象,其中包含要执行的操作的上下文/代码。 但是,猜测这是不可能的。 我最终编组了一个普通的ruby对象,其中包含程序运行后要执行的操作的说明。

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

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

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

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

然后用eval执行它

 eval code 

这将返回rubylamda。

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

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

如果您对使用Ruby2Ruby获取Ruby代码的字符串版本感兴趣,您可能会喜欢这个post 。

试试ruby2ruby

我发现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

如果将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}”)

曾几何时,这可以使用ruby-internal gem( https://github.com/cout/ruby-internal ),例如:

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

有一些警告,但它已经很多年了,我不记得细节了。 作为一个例子,我不确定如果一个变量是它被转储的绑定中的dynvar,并且绑定中的本地是重新绑定的,那么会发生什么。 序列化AST(在MRI上)或字节码(在YARV上)是非常重要的。

以上代码适用于YARV(最高1.9.3)和MRI(最高1.8.7)。 没有理由不能通过少量的努力使它在Ruby 2.x上工作。