3

我有机会在 StackOverflow 中环顾四周,发现了同样的问题,我试图从 Ruby Koans 中更好地理解这个问题(Ruby Koans:类定义上的显式作用域第 2 部分)。

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

def test_who_wins_with_both_nested_and_inherited_constants
  assert_equal 2, MyAnimals::Bird.new.legs_in_bird
end

# QUESTION: Which has precedence: The constant in the lexical scope,
# or the constant from the inheritance heirarachy?

# ------------------------------------------------------------------

class MyAnimals::Oyster < Animal
  def legs_in_oyster
    LEGS
  end
end

def test_who_wins_with_explicit_scoping_on_class_definition
  assert_equal 4, MyAnimals::Oyster.new.legs_in_oyster
end

# QUESTION: Now Which has precedence: The constant in the lexical
# scope, or the constant from the inheritance heirarachy?  Why is it
# different than the previous answer?

根据链接中的解释,其他人(包括我自己)的主要困惑似乎是因为类定义:

class MyAnimals::Oyster < Animal
  # stuff goes in here
end

我最初的想法是 MyAnimals::Oyster 意味着 Oyster 类是在 MyAnimals 中定义的。换句话说,我认为上面的代码类似于下面的代码:

class MyAnimals
  class Oyster < Animal
    # stuff goes in here
  end
end

为了测试我的想法,我在 IRB 中做了以下操作:

class MyAnimals
  LEGS = 2

  class Bird < Animal
    def legs_in_bird
      LEGS
    end
  end
end

class MyAnimals::Oyster # You should notice that I'm not inheriting from Animal anymore
  def legs_in_oyster
    LEGS
  end
end

如果我的推理是正确的,那么我希望下面的代码返回 2

MyAnimals::Oyster.new.legs_in_oyster # => NameError: uninitialized constant MyAnimals::Oyster::LEGS

由于这不返回 2,有人可以向我解释为什么它不返回 2?

编辑:我忽略了添加 Animal 类;这里是:

class Animal
  LEGS = 4
  def legs_in_animal
    LEGS
  end

  class NestedAnimal
    def legs_in_nested_animal
      LEGS
    end
  end
end
4

2 回答 2

3

回到 Ruby 搜索值的顺序。第 1 步是“封闭范围”。当您定义这样的嵌套类时

class Animal
    class Bird
    end
end

'Bird' 包含在 'Animal' 范围内。

当你这样做时

class Animal
end

class Animal::Bird
end

尽管您已将“Bird”定义为嵌套在“Animal”中,但在您定义“Bird”时,封闭范围是全局范围。返回 IRB 并定义LEGS = 0(在全局范围内)并再次尝试您的 Oyster。

于 2013-08-21T20:56:23.650 回答
2

在严格词法/静态范围的语言(如 C++ 或 Java)中,通过简单地检查当前范围来解析标识符,将范围向上提升一个范围并重复直到达到最基础的范围。例如:如果您的示例是 C++,则首先在调用类 Bird/Oyster 中搜索 LEGS,然后是 Animal,然后是 My Animal。

Ruby 有一种动态作用域。每个对象,即使它位于同一个“位置”,也可以有自己的范围查找顺序,具体取决于它在运行时的定义或创建方式。您可以将每种方法视为具有要搜索的范围堆栈,并且它的定义方式会将新范围推入该堆栈。

由于 Bird 的定义方式(BaseScope 不是它的真实名称,您没有提供足够的代码来提供此名称),以及BaseScope::MyAnimals::Bird搜索BaseScope::MyAnimals::Animal解析名称。BaseScope::MyAnimalsBaseScope

虽然第一只牡蛎只得到BaseScope::MyAnimals::Oyster,BaseScope::MyAnimals::AnimalBaseScope

没有继承的牡蛎变得更少,只是BaseScope::MyAnimals::OysterBaseScope

此示例中每次使用 class 关键字和继承都会将另一个作用域推送到作用域堆栈上,以便搜索其内容。因此,class MyAnimals::Oyster仅使用将一个条目推入此堆栈。

编辑

为简单起见,我省略了方法legs_in_oyster。这是一个可以搜索的范围。它的简单定义是不言自明的,包括它会给这个答案添加很多无用的文本。

为简单起见,我还省略了全局范围。我知道 Koans 在 BaseScope 和全局范围之间或之间至少有一个范围。

于 2013-08-21T20:51:28.890 回答