【问题标题】:Ruby Koans - Continuation of Lexical Scope vs Inheritance HierarchyRuby Koans - 词汇范围与继承层次结构的延续
【发布时间】:2013-02-26 23:33:30
【问题描述】:

我有机会在 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

【问题讨论】:

  • 那么Animal 是什么?不是每个人都知道 koans。
  • 抱歉,我现在添加了 Animal 类。

标签: ruby inheritance namespaces lexical-scope


【解决方案1】:

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

class Animal
    class Bird
    end
end

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

当你这样做时

class Animal
end

class Animal::Bird
end

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

【讨论】:

    【解决方案2】:

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

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

    由于 Bird 的定义方式,它得到了(BaseScope 不是它的真实名称,您没有提供足够的代码来提供这个)BaseScope::MyAnimals::BirdBaseScope::MyAnimals::AnimalBaseScope::MyAnimalsBaseScope 来搜索解析名字。

    而第一个 Oyster 仅获得 BaseScope::MyAnimals::OysterBaseScope::MyAnimals::AnimalBaseScope

    没有继承的 Oyster 就更少了,只有 BaseScope::MyAnimals::OysterBaseScope

    在本例中,每次使用 class 关键字和继承都会将另一个作用域推送到作用域堆栈上以检查其内容以进行搜索。所以使用class MyAnimals::Oyster 只会将一个条目压入此堆栈。

    编辑

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

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

    【讨论】:

    • 我的 C++ 生锈了。你甚至可以定义一个嵌套类吗?
    • 是的,您可以在 C++ 中定义嵌套类。这是代码异味,边缘案例编译器通常应避免并区别对待。
    猜你喜欢
    • 2013-02-13
    • 2014-12-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-06-16
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多