15

proc フレーバーの Proc をラムダ フレーバーの Proc に変換することは可能ですか?

少なくとも 1.9.2 では、これが機能しないことに少し驚いています。

my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
4

5 に答える 5

21

これは追跡するのが少し難しいものでした。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
于 2010-06-01T00:54:52.973 に答える
6

問題なく 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_execclass_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#greetconvert_to_lambdaでは動作しないため、それは間違っていると結論付けていclass_execます。

于 2014-05-08T02:18:28.343 に答える
3

proc をラムダに変換するクロス Ruby 互換ライブラリ: https://github.com/schneems/proc_to_lambda

Gem: http://rubygems.org/gems/proc_to_lambda

于 2013-07-12T18:42:03.347 に答える