这个问题是受this one启发的。Proc::new
有一个选项,可以在方法内部不加阻塞的情况下进行调用:
当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。 最佳答案
在Ruby Issue Tracker上对此行为进行了一些讨论,请参阅Feature #10499: Eliminate implicit magic in Proc.new
and Kernel#proc
。
这是YARV的实现工件:YARV将块插入到全局VM堆栈中,而Proc::new
只是从堆栈中最顶层的块创建Proc
。因此,如果您碰巧从用块调用的方法中调用Proc.new
,它将很乐意捕获堆栈顶部的任何块,而无需检查它的来源。不知何故,在某个时间的薄雾中,这个“偶然的伪像”(我宁愿称它为错误)(我们称它为“错误”)成为了有据可查的功能。 JRuby的开发人员(大概是Rubinius,Opal,MagLev等)的开发人员宁愿放弃该功能。
由于大多数其他实现的工作方式完全不同,因此此行为在YARV上“免费”出现,这使得块和Proc::new
在其他实现上的成本都变得昂贵,并且禁止了可能的优化(这对YARV不利,因为YARV并未进行优化) 。
关于ruby - 传递给 `instance_exec`时如何执行proc,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/40739398/