Как мне маршалировать лямбду (Proc) в Ruby?
-
09-06-2019 - |
Вопрос
Джо Ван Дайк запросил список рассылки Ruby:
Привет,
В Ruby, я полагаю, вы не можете маршализовать объект lambda / proc, верно?Есть что можно в Lisp или другие языки?
То, что я пытался сделать:
l = lamda { ... }
Bj.submit "/path/to/ruby/program", :stdin => Marshal.dump(l)
Итак, я отправляю BackgroundJob объект lambda, который содержит контекст / код для того, что нужно сделать.Но, думаю, это было невозможно.Я закончил маршалинг обычным объектом ruby, который содержал инструкции о том, что делать после запуска программы.
Джо
Решение
Вы не можете маршализовать лямбду или процедуру.Это связано с тем, что оба они считаются замыканиями, что означает, что они замыкаются вокруг памяти, в которой они были определены, и могут ссылаться на нее.(Чтобы упорядочить их, вам пришлось бы упорядочить всю память, к которой они могли получить доступ на момент их создания.)
Однако, как указал Гай, вы можете использовать ruby2руби чтобы получить доступ к строке программы.То есть вы можете маршалировать строку, представляющую код ruby, а затем повторно оценить ее позже.
Другие советы
вы также можете просто ввести свой код в виде строки:
code = %{
lambda {"hello ruby code".split(" ").each{|e| puts e + "!"}}
}
затем выполните это с помощью eval
eval code
который вернет ruby lamda.
используя %{}
format экранирует строку, но закрывается только при несогласованной фигурной скобке.т. е.вы можете вставить фигурные скобки следующим образом %{ [] {} }
и она по-прежнему закрыта.
большинство средств выделения синтаксиса текста не понимают, что это строка, поэтому по-прежнему отображают обычную подсветку кода.
Если вы заинтересованы в получении строковой версии Ruby-кода с использованием Ruby2Ruby, вам может понравиться этот поток.
Попробуй ruby2руби
Я обнаружил, что 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)
Если 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.длина]
proc = eval("лямбда #{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
Есть некоторые оговорки, но прошло много лет, и я не могу вспомнить деталей.В качестве примера, я не уверен, что произойдет, если переменная является dynvar в привязке, где она сбрасывается, и локальной в привязке, где она повторно привязывается.Сериализация AST (на MRI) или байт-кода (на YARV) нетривиальна.
Приведенный выше код работает с YARV (до 1.9.3) и MRI (до 1.8.7).Нет никаких причин, по которым это нельзя было бы заставить работать на Ruby 2.x с небольшими усилиями.