传递给`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 源代码?

如何显示传递给刀片视图的数据[重复]

如何将执行查询的结果放入 ViewData 并传递给视图

AWS ECS - 如何将任务的执行角色传递给 Boto3?

如何将AWS实例的详细信息传递给jenkins作业,以便该作业在实例中ssh和执行

Node.js如何将数组索引传递给异步函数