传递给`instance_exec`时如何执行proc
Posted
技术标签:
【中文标题】传递给`instance_exec`时如何执行proc【英文标题】:How proc is executed when passed to `instance_exec` 【发布时间】:2017-04-05 23:35:38 【问题描述】:这个问题的灵感来自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
控制台中测试上面的代码:他们知道纯粹执行此代码时会出现故障,主要是因为他们修补了 procs。
【问题讨论】:
@fl00r MRI 2.1–2.3,我实际上相信所有版本在这里都表现相同。 我无法收到您的输出ruby 2.2.2p95 (2015-04-13 revision 50295) [x86_64-darwin14]
我收到了[1].each &λ; => [1]
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
。 [1].each &λ
#=> [1]
好的,结果我收到的输出是pry
-related。无论如何,问题仍然存在。
@AndreyDeineko 这是pry
故障;同样的原因迫使我认为Proc::new
被调用了。将此代码复制粘贴到某个文件中并ruby file.rb
它。
【参考方案1】:
在 Ruby 问题跟踪器上对此行为进行了一些讨论,请参阅 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没有优化)。
【讨论】:
“YARV 将指针推送到 VM 堆栈上的块”——这看起来几乎是我问题的一半答案。这是否意味着 1) 任何其他 ruby 实现为[1].each &λ
调用 Proc::new
和 2) 开发人员无法在 YARV 中干扰 [1].each &λ
,因为根本没有创建 Proc
实例? AFAIU,无论堆栈是否碰巧包含一个块作为“先到先出”,YARV 都会很乐意绕过任何其他 ruby 代码/包装器/其他东西来执行它,对吧?
不确定。我的回答是基于对 Charles Nutter 的一篇文章的模糊记忆,我现在在我的回答中找到并链接到了这篇文章。但是,它没有解释例如如何JRuby 做到了。
知道了,谢谢。如果您将答案更新为“可能应用任何解决方案来干预传递给方法的块,它似乎不是跨 VM 实现”,我将不胜感激。以上是关于传递给`instance_exec`时如何执行proc的主要内容,如果未能解决你的问题,请参考以下文章
在哪里可以找到像 instance_exec 这样的 Native Ruby 源代码?
AWS ECS - 如何将任务的执行角色传递给 Boto3?