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

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

Привет,

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

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

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

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

Джо

7 ответов

Решение

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

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

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

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

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

eval code

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

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

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

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

Попробуйте ruby2ruby

Я нашел 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)

Когда-то это было возможно с использованием гема 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 при небольших затратах.

Если в файле определен 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}")

Другие вопросы по тегам