问题的灵感来自this one。
Proc::new
可以选择在方法中没有块的情况下调用:
只有在带有附加块的方法中,才能在没有块的情况下调用
Proc::new
,在这种情况下,该块将转换为Proc
对象。
当proc
/ lambda
实例作为代码块传递时,正在创建Proc
的新实例:
Proc.singleton_class.prepend(Module.new do
def new(*args, &cb)
puts "PROC #{[block_given?, cb, *args].inspect}"
super
end
end)
Proc.prepend(Module.new do
def initialize(*args, &cb)
puts "INIT #{[block_given?, cb, *args].inspect}"
super
end
def call(*args, &cb)
puts "CALL #{[block_given?, cb, *args].inspect}"
super
end
end)
λ = ->(*args) { }
[1].each &λ
#⇒ [1]
正如人们可能看到的那样,Proc::new
的呼叫既未发生,也未呼叫Proc#initialize
和/或Proc#call
。
问题是: ruby如何在引擎盖下创建和执行块包装器?
NB 不要在pry
/ irb
控制台中测试上面的代码:他们知道有纯粹执行此问题的故障,主要是因为他们修补了proc。 / p>
答案 0 :(得分:2)
在Ruby Issue Tracker上已经讨论过这种行为,请参阅Feature #10499: Eliminate implicit magic in Proc.new
and Kernel#proc
。
这是YARV的实现工件:YARV在全局VM堆栈上推送块,而Proc::new
只是从堆栈的最顶层块创建Proc
。因此,如果您碰巧在使用块调用的方法中调用Proc.new
,它将很乐意抓住堆栈顶部的任何块,而无需检查它来自何处。不知何故,在某个地方,在时间的迷雾中,这个(让他们称之为)"偶然的神器" (我实际上把它称为一个bug)成为一个记录的功能。 JRuby(可能是Rubinius,Opal,MagLev等)的开发人员宁愿摆脱这一功能。
由于大多数其他实现的工作方式完全不同,因此这种行为免费提供了#34;在YARV上,使得两个块和Proc::new
在其他实现上的成本更高,并禁止可能的优化(这对YARV没有影响,因为YARV没有优化)。