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のドキュメントを見ると、procsと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
クラスの汚染を避けたい場合は、匿名オブジェクトにシングルトンメソッドを定義するだけで、procaを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 では機能しません。selfObject.newdefine_singleton_methodto_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 &qqは秘密裏に にバインドされたメソッド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