2013-02-27 137 views
32

這是一個延續這種原始的SO問題:Using "::" instead of "module ..." for Ruby namespacing紅寶石 - 詞法範圍VS繼承

在原來的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 

有人可以提供一些解釋,爲什麼第一個電話返回555,爲什麼第二個電話返回123?

+1

威爾森,你覺得是值得的賞金其回答以下?謝謝 – rainkinz 2013-03-07 21:37:40

+0

提示:在你的代碼中添加兩個「puts Module.nesting」。 另請參閱:http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ – 2013-03-08 15:22:14

回答

33

你能想到的module Somethingclass Somethingdef something每次出現的「網關」到一個新的範圍。當Ruby搜索被引用的名稱的定義時,它首先在當前作用域(方法,類或模塊)中查找,如果沒有找到,它將通過包含「網關」和搜索的每個那裏的範圍。

在您的例子方法baz被定義爲

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

所以,當試圖確定FOO值,第一類Bar檢查,由於Bar不包含FOO搜索向上移動通過「class Bar網關」進入Foo模塊,該模塊是包含範圍。 Foo確實包含常量FOO(555),所以這是您看到的結果。

方法glorf被定義爲:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

這裏的「網關」是class Foo::Bar,所以當FOO沒有內部Bar發現「網關」穿過Foo模塊和直入頂層,那裏有另外一個FOO(123)這就是顯示的內容。

說明了如何使用class Foo::Bar創建一個單一的「網關」,跳過的Foo範圍,但module Foo; class Bar ...打開兩個單獨的「網關」

+3

順便說一句。網關術語。在Ruby源代碼中,似乎有人可以稱之爲「範圍堆棧」。所以每次你輸入'class'或'module',一個新的作用域被壓入這個棧。當Ruby搜索變量或常量時,它會自下而上地查詢這個堆棧,如果在上面找不到變量,則結束於頂層'main'。在'class Foo :: Bar'的情況下,它確實應該將兩個範圍推到堆棧上('Foo'和'Bar'),但它只推動一個,因此我們得到了「問題」。 – Casper 2013-03-04 11:32:01

+0

這與原始答案有何不同? – rainkinz 2013-03-04 15:30:15

+1

@Casper有道理。我在某處(某處不記得在哪裏)讀到了關於「網關」的想法,以此作爲思考發生了什麼的一種方式,但我沒有看到實現。我想這個行爲的一個解釋是它允許你打開一個嵌套類(以猴子修補它),而不需要擔心封閉的範圍干擾。 – matt 2013-03-08 15:41:04

5

哇,很好的問題。我可以想出最好的答案就是在這種情況下,您正在使用模塊來定義一個名稱空間。

檢查了這一點:

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 

所以我的想法是,當你定義:

module Foo 
    FOO = 555 
end 

你在Foo命名空間創建FOO。所以當你在這裏使用它時:

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

你在Foo命名空間。然而,當你引用它:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

FOO從默認命名空間來(由::FOO如圖所示)。

+0

感謝您的例子!除了一點之外,這是有道理的:當你定義Foo :: Bar類時,你是不是將它命名爲Foo? 「Foo :: Bar」中的「Foo ::」部分是否暗示你正在爲該類命名空間? – wmock 2013-02-27 19:34:51

+0

Foo :: Bar.new.glorf返回123對我來說很難理解,當Foo :: Bar.new.baz返回555. – wmock 2013-02-27 19:36:10

+1

我也會這麼想,但它看起來像是什麼,你正在命名空間Bar (在Foo下)顯式地由Foo :: Bar和該上下文中的其他內容仍然來自默認命名空間。 – rainkinz 2013-02-27 19:37:31

0

第一個電話:

puts Foo::Bar.new.baz # -> 555 

打印調用類的實例的方法巴茲的結果美孚::酒吧

通知,富::酒吧#巴茲定義實際上是FOO上的封閉。下面的Ruby的範圍規則:

  1. FOO中搜索在美孚::酒吧(類,而不是實例)範圍,但沒有找到,
  2. FOO中搜索在封閉範圍美孚(因爲我們是模塊定義範圍內),並且在那裏發現(555)

第二呼叫:

puts Foo::Bar.new.glorf # -> 123 

打印調用類的實例的方法glorf的結果美孚::酒吧

通知,富::酒吧#glorf定義這個時候也對FOO封閉,但如果我們按照紅寶石的範圍規則,你會發現,當值這個時候關閉是::通過以下方式FOO(頂層範圍FOO):

  1. FOO中搜索在美孚::酒吧(類,而不是實例)命名空間,它沒有發現
  2. FOO搜索在封閉的範圍(「頂層」),它是發現那裏(123)
0

glorf是類Foo的方法,在=> [Foo, Module, Object, Kernel, BasicObject]

範圍(即,在默認/主模塊),FOO被分配123

模塊的Foo被定義爲

module Foo 
    FOO = 555 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

在其中方法巴茲屬於Bar類模塊美孚=> [Bar, Foo, Object, Kernel, BasicObject]

,並在該範圍FOO被分配了555

+0

上面提到的'main'是irb的神器,實際上'FOO = 123'是進入Object類的頂層方法。 – aug2uag 2013-03-09 01:06:10