proc フレーバーの Proc をラムダ フレーバーの Proc に変換することは可能ですか?
少なくとも 1.9.2 では、これが機能しないことに少し驚いています。
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
proc フレーバーの Proc をラムダ フレーバーの Proc に変換することは可能ですか?
少なくとも 1.9.2 では、これが機能しないことに少し驚いています。
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
これは追跡するのが少し難しいものでした。Proc#lambda?
1.9のドキュメントを見ると、proc
sとsの違いについてかなり長い議論がありlamdba
ます。
結局のところ、lambda
強制は正しい数の引数を強制し、は強制proc
しないということです。そして、そのドキュメントから、procをラムダに変換する唯一の方法がこの例に示されています。
define_method
非ラムダProcオブジェクトが指定されている場合でも、常にトリックなしでメソッドを定義します。これは、トリックが保存されない唯一の例外です。class C define_method(:e, &proc {}) end C.new.e(1,2) => ArgumentError C.new.method(:e).to_proc.lambda? => true
クラスの汚染を避けたい場合は、匿名オブジェクトにシングルトンメソッドを定義するだけで、proc
aをlambda
:に強制できます。
def convert_to_lambda &block
obj = Object.new
obj.define_singleton_method(:_, &block)
return obj.method(:_).to_proc
end
p = Proc.new {}
puts p.lambda? # false
puts(convert_to_lambda(&p).lambda?) # true
puts(convert_to_lambda(&(lambda {})).lambda?) # true
問題なく proc をラムダに変換することはできません。マーク・ルシャコフによる答えは、 になるself
ため、ブロック内のの値を保持しません。Pawel Tomulik による回答は、Ruby 2.1 では機能しません。self
Object.new
define_singleton_method
to_lambda2
:_.to_proc
私の答えも間違っています:
def convert_to_lambda &block
obj = block.binding.eval('self')
Module.new.module_exec do
define_method(:_, &block)
instance_method(:_).bind(obj).to_proc
end
end
self
ブロック内の値を保持します。
p = 42.instance_exec { proc { self }}
puts p.lambda? # false
puts p.call # 42
q = convert_to_lambda &p
puts q.lambda? # true
puts q.call # 42
しかし、それは失敗しますinstance_exec
:
puts 66.instance_exec &p # 66
puts 66.instance_exec &q # 42, should be 66
block.binding.eval('self')
正しいオブジェクトを見つけるために使用する必要があります。メソッドを匿名モジュールに配置したため、クラスが汚染されることはありません。次に、メソッドを正しいオブジェクトにバインドします。これは、オブジェクトにモジュールが含まれていなくても機能します! バインドされたメソッドはラムダを作成します。
66.instance_exec &q
q
は秘密裏に にバインドされたメソッド42
であり、メソッドを再バインドできないため、失敗しますinstance_exec
。拡張q
してバインドされていないメソッドを公開し、バインドさinstance_exec
れていないメソッドを別のオブジェクトにバインドするように再定義することで、これを修正できます。それでも、それでも失敗しますmodule_exec
。class_exec
class Array
$p = proc { def greet; puts "Hi!"; end }
end
$q = convert_to_lambda &$p
Hash.class_exec &$q
{}.greet # undefined method `greet' for {}:Hash (NoMethodError)
問題は、 が を定義し、 をHash.class_exec &$q
定義Array#greet
しないことHash#greet
です。($q
密かに匿名モジュールのメソッドですが、匿名モジュールでArray
はなく でメソッドを定義しています。) 元の proc では、Hash.class_exec &$p
を定義しますHash#greet
。convert_to_lambda
では動作しないため、それは間違っていると結論付けていclass_exec
ます。
proc をラムダに変換するクロス Ruby 互換ライブラリ: https://github.com/schneems/proc_to_lambda