Ruby - 词法范围与继承

wmo*_*ock 33 ruby inheritance lexical-scope

这是原始SO问题的延续:使用"::"而不是"module ..."来表示Ruby命名空间

在最初的SO问题中,这里是我仍然无法理解的情景:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end

class Foo::Bar
  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
Run Code Online (Sandbox Code Playgroud)

有人可以提供一些解释为什么第一个呼叫返回555以及为什么第二个呼叫返回123?

mat*_*att 34

你能想到的每一个外观module Something,class Something或者def something作为一个"门户"进入一个新的范围.当Ruby搜索已被引用的名称的定义时,它首先查找当前作用域(方法,类或模块),如果找不到它,它将返回每个包含"gateway"并搜索那里的范围.

在您的示例中,该方法baz定义为

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

因此,在尝试确定值时FOO,首先Bar检查类,并且由于Bar不包含FOO搜索,因此通过" class Bar网关" 向上移动到Foo包含范围的模块中.Foo确实包含一个常量FOO(555),所以这是你看到的结果.

该方法glorf定义为:

class Foo::Bar
  def glorf
    puts FOO
  end
end
Run Code Online (Sandbox Code Playgroud)

这里的"网关"是class Foo::Bar,所以当在"网关" FOO内部找不到时,Bar通过Foo模块并直接进入顶层,其中有另一个FOO(123)显示的内容.

请注意如何使用class Foo::Bar创建单个"网关",跳过范围Foo,但module Foo; class Bar ...打开两个单独的"网关"

  • 顺便说一句.网关术语.在Ruby源代码中,似乎可以称之为"范围堆栈".因此,每次键入`class`或`module`时,新的作用域都会被压入此堆栈.当Ruby然后搜索变量或常量时,它从下到上查询这个堆栈,如果它没有在上升中找到变量,则以顶层`main`结束.在`class Foo :: Bar`的情况下,它确实应该将两个范围推入堆栈(包括'Foo`和`Bar`),但它只推送一个,因此我们得到了"问题". (3认同)

rai*_*inz 6

哇,好问题.我能想出的最佳答案是在这种情况下,您正在使用模块来定义命名空间.

看一下这个:

FOO = 123

module Foo
  FOO = 555
end

module Foo
  class Bar
    def baz
      puts FOO
    end

    def glorf3
      puts ::FOO
    end
  end
end

class Foo::Bar
  def glorf2
    puts Foo::FOO
  end

  def glorf
    puts FOO
  end
end

puts Foo::Bar.new.baz    # -> 555
puts Foo::Bar.new.glorf  # -> 123
puts Foo::Bar.new.glorf2  # -> 555
puts Foo::Bar.new.glorf3  # -> 123
Run Code Online (Sandbox Code Playgroud)

所以我的想法是,当你定义:

module Foo
  FOO = 555
end
Run Code Online (Sandbox Code Playgroud)

你正在创建FOO名称空间Foo.所以当你在这里使用它时:

module Foo
  class Bar
    def baz
      puts FOO
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

你在Foo命名空间.但是,当您在以下位置引用它时:

class Foo::Bar
  def glorf
    puts FOO
  end
end
Run Code Online (Sandbox Code Playgroud)

FOO来自默认命名空间(如图所示::FOO).

  • 对,这就是我的想法.希望其他人会发表评论.这真的很有意思...... (2认同)