Ruby Koans - 词汇范围与继承层次结构的延续

时间:2013-02-26 23:33:30

标签: ruby inheritance namespaces lexical-scope

我有机会在StackOverflow中查看并发现了同样的问题,我试图从Ruby Koans(Ruby Koans: explicit scoping on a class definition part 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

2 个答案:

答案 0 :(得分:4)

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

class Animal
    class Bird
    end
end

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

当你这样做时

class Animal
end

class Animal::Bird
end

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

答案 1 :(得分:1)

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

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

由于定义了Bird的方式(BaseScope不是它的真名,你没有提供足够的代码来提供这个)BaseScope::MyAnimals::BirdBaseScope::MyAnimals::AnimalBaseScope::MyAnimals和{{ 1}}搜索解析名称。

虽然第一个Oyster只获得BaseScopeBaseScope::MyAnimals::OysterBaseScope::MyAnimals::Animal

没有继承的牡蛎变得更少,只有BaseScopeBaseScope::MyAnimals::Oyster

在此示例中,每次使用class关键字和继承都会推动另一个范围检查其搜索内容的作用域堆栈。所以使用BaseScope只将一个条目推入此堆栈。

修改

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

我也忽略了全球范围的简单性。我知道Koans在BaseScope和全球范围内至少有一个范围。