Ruby: convert proc to lambda?
是否可以将一个proc-flavered proc转换为lambda-flavered proc?
至少在1.9.2中,有点惊讶于这不起作用:
1 2 3 | my_proc = proc {|x| x} my_lambda = lambda &p my_lambda.lambda? # => false! |
这个有点难找到。看一下1.9版的
归根结底,
define_method always defines a method without the tricks, even if a non-lambda Proc object is given. This is the only exception which the tricks are not preserved.
1
2
3
4
5 class C
define_method(:e, &proc {})
end
C.new.e(1,2) => ArgumentError
C.new.method(:e).to_proc.lambda? => true
如果要避免污染任何类,只需在匿名对象上定义一个singleton方法,以便将
1 2 3 4 5 6 7 8 9 10 11 | 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 |
不可能毫无困难地将过程转换为lambda。Mark Rushakoff的回答没有保留该区块中
我的回答也是错误的:
1 2 3 4 5 6 7 | 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 |
保留了该区块中
1 2 3 4 5 6 7 | 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 |
但在
1 2 | puts 66.instance_exec &p # 66 puts 66.instance_exec &q # 42, should be 66 |
我必须用
1 2 3 4 5 6 | 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) |
问题是,
以下是可能的解决方案:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | class Proc def to_lambda return self if lambda? # Save local reference to self so we can use it in module_exec/lambda scopes source_proc = self # Convert proc to unbound method unbound_method = Module.new.module_exec do instance_method( define_method( :_proc_call, &source_proc )) end # Return lambda which binds our unbound method to correct receiver and calls it with given args/block lambda do |*args, &block| # If binding doesn't changed (eg. lambda_obj.call) then bind method to original proc binding, # otherwise bind to current binding (eg. instance_exec(&lambda_obj)). unbound_method.bind( self == source_proc ? source_proc.receiver : self ).call( *args, &block ) end end def receiver binding.eval("self" ) end end p1 = Proc.new { puts"self = #{self.inspect}" } l1 = p1.to_lambda p1.call #=> self = main l1.call #=> self = main p1.call( 42 ) #=> self = main l1.call( 42 ) #=> ArgumentError: wrong number of arguments (1 for 0) 42.instance_exec( &p1 ) #=> self = 42 42.instance_exec( &l1 ) #=> self = 42 p2 = Proc.new { return"foo" } l2 = p2.to_lambda p2.call #=> LocalJumpError: unexpected return l2.call #=>"foo" |
应该在Ruby 2.1上工作+
用于将procs转换为lambdas的Cross Ruby兼容库:https://github.com/schneems/proc_到_lambda
杰姆:http://rubygems.org/gems/proc_to_lambda网站
上面的代码不能很好地与
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | # /tmp/test.rb def to_lambda1(&block) obj = Object.new obj.define_singleton_method(:_,&block) obj.method(:_).to_proc end def to_lambda2(&block) Object.new.define_singleton_method(:_,&block).to_proc end l1 = to_lambda1 do print"to_lambda1: #{self.class.name} " end print"l1.lambda?: #{l1.lambda?} " l2 = to_lambda2 do print"to_lambda2: #{self.class.name} " end print"l2.lambda?: #{l2.lambda?} " class A; end A.new.instance_exec &l1 A.new.instance_exec &l2 |
上面脚本的输出是:
1 2 3 4 | l1.lambda?: true l2.lambda?: true to_lambda1: Object to_lambda2: A |
实际上,我希望