Как мне организовать лямбду (Proc) в Ruby?

Джо Ван Дайк спросил список рассылки Ruby :

Здравствуй,

В Ruby, я полагаю, вы не можете маршалировать объект лямбда / процесс, верно? Это возможно в lisp или других языках?

Что я пытался сделать:

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

Итак, я отправляю BackgroundJob лямбда-объект, который содержит контекст / код для того, что делать. Но, думаю, это было невозможно. Я закончил маршалингом обычного объекта ruby, который содержал инструкции о том, что делать после запуска программы.

Джо

23.08.2008 04:22:44
7 ОТВЕТОВ
РЕШЕНИЕ

Вы не можете маршалировать лямбда или проц. Это потому, что оба они считаются замыканиями, что означает, что они закрывают память, в которой они были определены, и могут ссылаться на нее. (Для того, чтобы их маршалировать, вам нужно было бы маршалу всю память, к которой они могли получить доступ во время их создания.)

Однако, как отметил Гай, вы можете использовать ruby2ruby, чтобы взять строку программы. Таким образом, вы можете упорядочить строку, которая представляет код ruby, а затем пересмотреть ее позже.

21
16.02.2015 20:53:31
ruby2ruby работает только на 1.8, официального способа десериализации байт-кода 1.9 пока нет.
manveru 18.11.2009 09:00:28
Ruby2ruby некоторое время работает в MRI 1.9. Потрошитель тоже классный и поставляется с МРТ (начиная с 1.9).
Wojciech Kaczmarek 3.09.2013 11:36:08

Попробуй ruby2ruby

3
23.08.2008 04:24:49

Если вы заинтересованы в получении строковой версии кода Ruby с использованием Ruby2Ruby, вам может понравиться этот поток .

4
23.05.2017 11:54:05

Вы также можете просто ввести свой код в виде строки:

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

затем выполнить его с помощью eval

eval code

который вернет рубиновую лямду.

использование %{}формата экранирует строку, но закрывает только непревзойденную фигурную скобку. то есть вы можете вложить такие скобки, %{ [] {} }и они все еще закрыты.

большинство текстовых подсветок синтаксиса не понимают, что это строка, поэтому все равно отображают обычную подсветку кода.

12
3.10.2012 21:30:37

Я нашел proc_to_ast для лучшей работы: https://github.com/joker1007/proc_to_ast .

Работает наверняка в ruby ​​2+, и я создал PR для совместимости с ruby ​​1.9.3+ ( https://github.com/joker1007/proc_to_ast/pull/3 )

1
10.11.2015 00:07:27
Это интересное решение, хотя на самом деле это выглядит так, как будто он требует наличия источника для процесса на диске. Это имеет смысл; было бы нетривиально (возможно, невозможно) декомпилировать байт-код YARV обратно в AST. YARV отбрасывает исходный AST, когда он больше не нужен, но собственный компилятор байт-кода может сохранить исходный AST, что позволяет этой технике работать также с динамически генерируемыми процессами (т. Е. Созданными с помощью «eval»).
Paul Brannan 16.09.2018 10:17:22

Если в файле определен proc, U может получить местоположение файла proc, затем сериализовать его, а после десериализации использовать местоположение, чтобы снова вернуться к proc

proc_location_array = proc.source_location

после десериализации:

имя_файла = proc_location_array [0]

line_number = 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}")

0
11.04.2017 17:27:06

Когда-то это было возможно с использованием гема ruby-internal ( 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 (на МРТ) или байт-кода (на YARV) нетривиальна.

Приведенный выше код работает на YARV (до 1.9.3) и МРТ (до 1.8.7). Нет никаких причин, по которым он не может работать на Ruby 2.x с небольшими усилиями.

0
16.09.2018 10:11:32