是否可以將proc-flavored Proc轉換爲lambda-flavored Proc?Ruby:將proc轉換爲lambda?
有點驚訝,這是不行的,至少在1.9.2:
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
是否可以將proc-flavored Proc轉換爲lambda-flavored Proc?Ruby:將proc轉換爲lambda?
有點驚訝,這是不行的,至少在1.9.2:
my_proc = proc {|x| x}
my_lambda = lambda &p
my_lambda.lambda? # => false!
這一個是有點棘手追查。查看Proc#lambda?
for 1.9的文檔,關於proc
和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
到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
跨紅寶石compatiable庫用於將特效轉換爲lambda: https://github.com/schneems/proc_to_lambda
上面的代碼與instance_exec
不能很好地發揮,但我認爲有一個簡單的解決方案。在這裏,我有這說明了問題和解決方案的例子:
# /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}\n"
end
print "l1.lambda?: #{l1.lambda?}\n"
l2 = to_lambda2 do
print "to_lambda2: #{self.class.name}\n"
end
print "l2.lambda?: #{l2.lambda?}\n"
class A; end
A.new.instance_exec &l1
A.new.instance_exec &l2
to_lambda1
基本上是由馬克提出的實施,to_lambda2
是「固定」的代碼。
從上面的腳本的輸出是:
l1.lambda?: true
l2.lambda?: true
to_lambda1: Object
to_lambda2: A
事實上,我期望instance_exec
輸出A
,不Object
(instance_exec
應該改變綁定)。我不知道爲什麼它的工作方式不同,但我想define_singleton_method
返回尚未綁定到Object
的方法,並且Object#method
返回已綁定的方法。
它是不是可能將proc轉換爲lambda沒有麻煩。 Mark Rushakoff的答案並不保留塊中self
的值,因爲self
變成了Object.new
。 Pawel Tomulik的答案不能用於Ruby 2.1,因爲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')
找到正確的對象。我把我的方法放在一個匿名模塊中,所以它不會污染任何類。然後我將我的方法綁定到正確的對象上。儘管該對象從未包含該模塊,但它仍然有效!綁定方法產生一個lambda。
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
。
以下是可能的解決方案:
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"
應該紅寶石2.1+
謝謝合作!非常有幫助:) define_method最終產生了lambda的事實促成了我的困惑。 – 2010-06-01 12:47:33
有趣的問題時間:你如何在jruby中做到這一點? – Schneems 2012-11-05 19:50:47
回答我的有趣問題:http://stackoverflow.com/questions/13239338/convert-bloc-to-lambda-in-jruby – Schneems 2013-06-10 17:49:33