简体   繁体   English

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

[英]How do I marshal a lambda (Proc) in Ruby?

Joe Van Dyk asked the Ruby mailing list : Joe Van Dyk 询问了Ruby邮件列表

Hi, 嗨,

In Ruby, I guess you can't marshal a lambda/proc object, right? 在Ruby中,我猜你不能编组一个lambda / proc对象,对吗? Is that possible in lisp or other languages? 在lisp或其他语言中这可能吗?

What I was trying to do: 我想做什么:

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

So, I'm sending BackgroundJob a lambda object, which contains the context/code for what to do. 所以,我正在向BackgroundJob发送一个lambda对象,该对象包含要执行的操作的上下文/代码。 But, guess that wasn't possible. 但是,猜测这是不可能的。 I ended up marshaling a normal ruby object that contained instructions for what to do after the program ran. 我最终编组了一个普通的ruby对象,其中包含程序运行后要执行的操作的说明。

Joe

You cannot marshal a Lambda or Proc. 你不能编组Lambda或Proc。 This is because both of them are considered closures, which means they close around the memory on which they were defined and can reference it. 这是因为它们都被认为是闭包,这意味着它们靠近定义它们的内存并且可以引用它。 (In order to marshal them you'd have to Marshal all of the memory they could access at the time they were created.) (为了编组它们,你必须将它们在创建时可以访问的所有内存编组。)

As Gaius pointed out though, you can use ruby2ruby to get a hold of the string of the program. 正如Gaius指出的那样,你可以使用ruby2ruby来获取程序的字符串。 That is, you can marshal the string that represents the ruby code and then reevaluate it later. 也就是说,您可以封送代表ruby代码的字符串,然后再重新评估它。

you could also just enter your code as a string: 你也可以输入你的代码作为字符串:

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

then execute it with eval 然后用eval执行它

eval code

which will return a ruby lamda. 这将返回红宝石lamda。

using the %{} format escapes a string, but only closes on an unmatched brace. 使用%{}格式转义字符串,但仅在不匹配的大括号上关闭。 ie you can nest braces like this %{ [] {} } and it's still enclosed. 即你可以像这个%{ [] {} }那样嵌套大括号,它仍然是封闭的。

most text syntax highlighters don't realize this is a string, so still display regular code highlighting. 大多数文本语法荧光笔没有意识到这是一个字符串,所以仍然显示常规代码突出显示。

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

试试ruby2ruby

I've found proc_to_ast to do the best job: https://github.com/joker1007/proc_to_ast . 我发现proc_to_ast做得最好: https//github.com/joker1007/proc_to_ast

Works for sure in ruby 2+, and I've created a PR for ruby 1.9.3+ compatibility( https://github.com/joker1007/proc_to_ast/pull/3 ) 确实在ruby 2+中工作,我为ruby 1.9.3+兼容性创建了一个PR( https://github.com/joker1007/proc_to_ast/pull/3

If proc is defined into a file, U can get the file location of proc then serialize it, then after deserialize use the location to get back to the proc again 如果将proc定义到文件中,U可以获取proc的文件位置然后序列化它,然后在反序列化后使用该位置再次返回proc

proc_location_array = proc.source_location proc_location_array = proc.source_location

after deserialize: 反序列化后:

file_name = proc_location_array[0] file_name = proc_location_array [0]

line_number = proc_location_array[1] line_number = proc_location_array [1]

proc_line_code = IO.readlines(file_name)[line_number - 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_hash_string = proc_line_code [proc_line_code.index(“{”).. proc_line_code.length]

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

Once upon a time, this was possible using ruby-internal gem ( https://github.com/cout/ruby-internal ), eg: 曾几何时,这可能是使用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

There are some caveats, but it has been many years and I cannot remember the details. 有一些警告,但它已经很多年了,我不记得细节了。 As an example, I'm not sure what happens if a variable is a dynvar in the binding where it is dumped and a local in the binding where it is re-bound. 作为一个例子,我不确定如果一个变量是它被转储的绑定中的dynvar和绑定中的一个本地被重新绑定,会发生什么。 Serializing an AST (on MRI) or bytecode (on YARV) is non-trivial. 序列化AST(在MRI上)或字节码(在YARV上)是非平凡的。

The above code works on YARV (up to 1.9.3) and MRI (up to 1.8.7). 以上代码适用于YARV(最高1.9.3)和MRI(最高1.8.7)。 There's no reason why it cannot be made to work on Ruby 2.x, with a small amount of effort. 没有理由不能通过少量努力使它在Ruby 2.x上运行。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM