乔·范戴克 询问 Ruby 邮件列表:

你好,

在 Ruby 中,我猜你不能封送 lambda/proc 对象,对吗?在LISP或其他语言中可能可以吗?

我想做什么:

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

因此,我正在向BackgroundJob发送一个lambda对象,该对象包含有关做什么的上下文/代码。但是,我猜这是不可能的。我最终建立了一个普通的红宝石对象,该对象包含有关该程序运行后要做什么的说明。

有帮助吗?

解决方案

您无法封送 Lambda 或 Proc。这是因为它们都被视为闭包,这意味着它们围绕定义它们的内存封闭并可以引用它。(为了整理它们,您必须整理它们在创建时可以访问的所有内存。)

正如盖乌斯指出的那样,你可以使用 红宝石2红宝石 获取程序的字符串。也就是说,您可以整理表示 ruby​​ 代码的字符串,然后稍后重新评估它。

其他提示

您也可以仅以字符串形式输入代码:

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

然后用 eval 执行它

eval code

这将返回一个 ruby​​ lamda。

使用 %{} format 对字符串进行转义,但仅以不匹配的大括号结束。IE。你可以像这样嵌套大括号 %{ [] {} } 而且它仍然是封闭的。

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

如果您有兴趣使用 Ruby2Ruby 获取 Ruby 代码的字符串版本,您可能会喜欢 这个线程.

我发现 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被定义到一个文件中,你可以获取proc的文件位置然后将其序列化,然后在反序列化后使用该位置再次返回到proc

proc_location_array = proc.source_location

反序列化后:

文件名 = proc_location_array[0]

行号 = proc_location_array[1]

proc_line_code = IO.readlines(文件名)[行号 - 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 }    #=> #<Proc>
s = Marshal.dump(p)   #=> #<String>
u = Marshal.load(s)   #=> #<UnboundProc>
p2 = u.bind(binding)  #=> #<Proc>
p2.call()             #=> 2

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

上面的代码适用于 YARV(最高 1.9.3)和 MRI(最高 1.8.7)。没有理由不让它在 Ruby 2.x 上工作,只需付出少量的努力。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top