Ruby でラムダ (Proc) をマーシャリングするにはどうすればよいですか?
-
09-06-2019 - |
質問
ジョー・ヴァン・ダイク Rubyのメーリングリストに質問した:
こんにちは、
Ruby では、lambda/proc オブジェクトをマーシャリングできないと思います。それはLISPまたは他の言語で可能ですか?
私がやろうとしていたこと:
l = lamda { ... }
Bj.submit "/path/to/ruby/program", :stdin => Marshal.dump(l)
だから、私はbackgroundjobに何をすべきかのコンテキスト/コードを含むラムダオブジェクトを送信します。しかし、それは不可能だったと思います。私は、プログラムが実行された後に何をすべきかについての指示を含む通常のルビーオブジェクトをマーシャリングすることになりました。
ジョー
解決
Lambda または Proc をマーシャリングすることはできません。これは、両方ともクロージャとみなされるためです。つまり、それらが定義されたメモリの周りを閉じて、それを参照できることを意味します。(それらをマーシャリングするには、それらが作成されたときにアクセスできたすべてのメモリをマーシャリングする必要があります。)
ただし、Gaius が指摘したように、次を使用できます。 ルビー2ルビー プログラムの文字列を取得します。つまり、Ruby コードを表す文字列をマーシャリングし、後で再評価できます。
他のヒント
コードを文字列として入力することもできます。
code = %{
lambda {"hello ruby code".split(" ").each{|e| puts e + "!"}}
}
それを eval で実行します
eval code
これは Ruby lamda を返します。
を使用して %{}
format は文字列をエスケープしますが、一致しない中括弧でのみ閉じます。つまりこのように中括弧を入れ子にすることができます %{ [] {} }
そしてまだ閉じ込められたままです。
ほとんどのテキスト構文ハイライトはこれが文字列であることを認識しないため、通常のコードのハイライトを表示します。
Ruby2Ruby を使用して Ruby コードの文字列バージョンを取得することに興味がある場合は、次のようにするとよいでしょう。 このスレッド.
試す ルビー2ルビー
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 がファイルに定義されている場合、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("ラムダ #{proc_hash_string}")
かつて、これは Ruby 内部の 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 上で動作させることができない理由はありません。