2013-02-27 27 views
32

Jest to kontynuacja ta oryginalna SO pytanie: Using "::" instead of "module ..." for Ruby namespacingRuby - leksykalna zakres vs dziedziczenia

W pierwotnym SO pytanie, tutaj jest scenariusz przedstawiony które ciągle mam problemy ze zrozumieniem:

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 

Czy ktoś może podać jakieś wyjaśnienie, dlaczego pierwsze połączenie zwraca 555 i dlaczego drugie połączenie zwraca 123?

+1

Willson, co odpowiedzieć poniżej myślisz jest godna laska? Dzięki – rainkinz

+0

Podpowiedź: dodaj "puts Module.nesting" po dwóch umieszczonych w twoim kodzie. Zobacz także: http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ –

Odpowiedz

33

można myśleć o każdym pojawieniem module Something, class Something lub def something jako „brama” do nowego zakresu. Kiedy Ruby szuka definicji nazwy, do której się odwołuje, najpierw wyszukuje bieżący zakres (metodę, klasę lub moduł), a jeśli nie zostanie znaleziony, przejdzie przez każdy z nich zawierający "bramę" i wyszukiwanie zakres tam.

W przykładzie metoda baz jest zdefiniowany jako

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

Więc kiedy próbuje ustalić wartość FOO, pierwsza klasa Bar jest zaznaczone, a od Bar nie zawiera FOO poszukiwanie przesuwa się w górę przez bramkę "class Bar" do modułu Foo, który jest zasięgiem zawartości. Foo zawiera stałą FOO (555), więc jest to wynik, który widzisz.

Sposób glorf jest zdefiniowany jako:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

tutaj „brama” jest class Foo::Bar, więc gdy FOO nie znajduje się wewnątrz Bar „brama” przechodzi przez moduł Foo prosto do góry poziomie , gdzie jest inny FOO (123), który jest wyświetlany.

Uwaga jak przy użyciu class Foo::Bar tworzy pojedynczy „brama”, pomijając zakresu Foo, ale module Foo; class Bar ... otwiera dwa oddzielne „bram”

+3

Btw. termin bramy. W źródłach Ruby wydaje się, że można nazwać "stosem zakresów". Tak więc za każdym razem, gdy wpiszesz 'class' lub' module', nowy zakres zostanie wciśnięty na ten stos. Kiedy Ruby szuka zmiennych lub stałych, konsultuje ten stos od dołu do góry, kończąc na 'głównym' najwyższego poziomu, jeśli nie znalazł zmiennej w drodze do góry. W przypadku 'class Foo :: Bar' to naprawdę powinno wypchnąć DWA zakresy na stos (zarówno' Foo' i 'Bar'), ale tylko popycha jeden, stąd dostaniemy" problem ". – Casper

+0

Czym różni się to od pierwotnej odpowiedzi? – rainkinz

+1

@Casper ma sens. Czytałem gdzieś o idei "bramy" (nie pamiętam gdzie niestety) jako o sposobie myślenia o tym, co się dzieje, ale nie spojrzałem na implementację. Myślę, że jednym z wyjaśnień tego zachowania jest to, że pozwala otworzyć klasę zagnieżdżoną (do łatki małpy) bez martwienia się o zakłócający zakres oscyloskopu. – matt

5

Wow, świetne pytanie. Najlepszą odpowiedzią, jaką mogę wymyślić, jest użycie modułu do zdefiniowania przestrzeni nazw.

to sprawdzić:

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 

Więc moja myśl jest taka, że ​​podczas definiowania:

module Foo 
    FOO = 555 
end 

tworzysz FOO w przestrzeni nazw Foo. Więc kiedy go używać tutaj:

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

jesteś w przestrzeni nazw Foo. Jednak, kiedy się do niego w:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

FOO pochodzi z domyślnej przestrzeni nazw (jak pokazano ::FOO).

+0

dzięki za przykłady! ma to sens z wyjątkiem jednego punktu: kiedy definiujesz klasę Foo :: Bar, czy nie nazywasz jej Foo? Czy część "Foo ::" z "Foo :: Bar" nie oznacza, że ​​nazywasz tą klasą? – wmock

+0

Foo :: Bar.new.glorf powracający 123 jest dla mnie trudny do zrozumienia, kiedy Foo :: Bar.new.baz zwraca 555. – wmock

+1

Też bym to pomyślał, ale wygląda na to, (pod Foo) jawnie przez Foo :: Bar i wszystko inne w tym kontekście nadal pochodzi z domyślnej przestrzeni nazw. – rainkinz

0

pierwsze połączenie:

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

drukuje wynik wywoływanie metody baz z instancją klasy Foo :: Bar

zauważyć, że Foo :: Bar baz definicja jest faktycznie zamknięciem na FOO.Zasady zakres następujących Ruby:

  1. FOO jest poszukiwany w Foo :: Bar (klasa, a nie instancji) zakres, to nie zostanie znaleziony,
  2. FOO jest poszukiwany w załączając zakres Foo (bo jesteśmy w definicji modułu) i okaże się, że (555)

drugiego naboru:

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

drukuje wynik wywoływanie metody glorf z instancją klasy Foo :: Bar

zauważyć, że Foo :: Bar glorf definicja tym razem jest również zamknięcie na FOO, ale jeśli będziemy przestrzegać zasad zakres języka Ruby można zauważyć, że wartość zamknięty po tym czasie jest :: FOO (górny zakres poziom FOO) w następujący sposób:

  1. FOO jest poszukiwany w Foo :: Bar (klasa, a nie instancji) przestrzeni nazw, nie znaleziono
  2. FOO jest przeszukiwany w zakresie zamykającego („najwyższego poziomu”) i jest znaleziono tam (123)
0

glorf to metoda klasy Foo, w => [Foo, Module, Object, Kernel, BasicObject]

obrębie tego zakres (tj zalega/moduł główny), Foo przypisano 123

moduł Foo jest zdefiniowana jako

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

gdzie metoda baz należy do klasy Bar w module Foo => [Bar, Foo, Object, Kernel, BasicObject]

i w tym zakresie FOO nadano 555

+0

"Główna" wspomniana powyżej jest artefaktem irb, faktycznie 'FOO = 123' jest metodą najwyższego poziomu, która przechodzi w klasę Object. – aug2uag