9

Metaprogramming Ruby 2的第 2 章的“细化”部分中,我发现了以下 Ruby 代码:

class MyClass 
  def my_method
    "original my_method()"
  end

  def another_method 
    my_method
  end

end

module MyClassRefinement 
  refine MyClass do
    def my_method
      "refined my_method()"
    end 
  end
end

using MyClassRefinement
MyClass.new.my_method # => "refined my_method()"
MyClass.new.another_method # => "original my_method()" - How is this possible?

据作者说:

然而,调用another_method 可能会让你措手不及:即使你调用another_methodafter using,对my_method自身的调用也会发生在之前using——所以它调用了原始的、未优化的方法版本。

这完全把我绊倒了。

为什么要MyClass.new.another_method打印“original my_method()”,因为它using MyClassRefinement是在之后使用的,作者在这里想说什么?

谁能提供更直观/更好的解释?

谢谢。

4

1 回答 1

5

我能找到的最好的解释来自文档

细化在范围上是词法的。细化仅在调用using. 语句之前的任何代码using都不会激活细化。

这意味着您的优化方法必须在调用using. 方法调用的实际位置才是重要的,而不是如何调用方法或从何处调用方法。


这就是发生的事情。

  1. usingusing MyClassRefinement激活my_method细化。
  2. MyClass.new.my_method被执行。
  3. 从调用的确切点开始进行方法查找:

class C在为Ruby 检查实例查找方法时:

  • 如果细化对 激活C,则以相反的顺序激活它们
    • 来自细化的前置模块C
    • 细化为C
    • 改进后包含的模块C
  • 前置模块C
  • C
  • 包含的模块C
  1. 细化处于活动状态,并my_method从细化中返回代码"refined my_method()"
  2. MyClass.new.another_method被执行。
  3. 方法查找从调用的确切点开始。
  4. 细化在这个调用点处于活动状态,但another_method不是细化,因此 Rubyanother_method在类中MyClass查找并找到它。
  5. 在类 method 内部,找到并调用another_method该方法。my_method
  6. 方法查找从调用的确切点开始。
  7. 在调用点,没有激活的细化,因为没有调用到调用using 线的上方(即物理上之前)my_method。Ruby 继续在my_method类中MyClass查找并找到它。
  8. my_method从类方法返回代码"original my_method()"

我们可以做一个简单的比较。假设我file.rb用以下代码隔离了一个:

puts puppy
puppy = 'waggle'

puppy在定义之前不能使用。该变量是词法范围的,它的使用取决于其定义在 isolated 中的位置file.rb

类似地,在通过前一行(或源代码文件中物理上之前的某个位置)激活细化之前,不能调用细化。using细化是词法范围的。

来自维基百科

在具有词法作用域(也称为静态作用域)的语言中,名称解析取决于源代码中的位置和词法上下文,词法上下文由定义命名变量或函数的位置定义...

词法解析可以在编译时确定,也称为早期绑定,而动态解析通常只能在运行时确定,因此称为后期绑定


本文的最后一节将讨论您在改进方面的具体问题。作者还解释了using语句在文件中的物理位置如何确定细化是否处于活动状态。

于 2017-10-07T20:52:26.883 回答