2013-02-27 21 views
32

Đây là một sự tiếp nối gốc SO câu hỏi này: Using "::" instead of "module ..." for Ruby namespacingRuby - phạm vi từ vựng vs Inheritance

Trong SO câu hỏi ban đầu, đây là kịch bản được trình bày mà tôi vẫn thấy khó hiểu:

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 

Ai đó có thể cung cấp một số lời giải thích đằng sau lý do tại sao cuộc gọi đầu tiên trả lại 555 và tại sao cuộc gọi thứ hai trả về 123?

+1

Willson, mà trả lời dưới đây bạn có nghĩ là xứng đáng với tiền thưởng? Cảm ơn – rainkinz

+0

Gợi ý: thêm "đặt Module.nesting" sau khi hai đặt trong mã của bạn. Xem thêm: http://coderrr.wordpress.com/2008/03/11/constant-name-resolution-in-ruby/ –

Trả lời

33

Bạn có thể nghĩ về từng hình thức của module Something, class Something hoặc def something làm “cổng” thành một phạm vi mới. Khi Ruby tìm kiếm định nghĩa của tên đã được tham chiếu, đầu tiên nó sẽ nhìn vào phạm vi hiện tại (phương thức, lớp hoặc mô-đun), và nếu nó không được tìm thấy ở đó, nó sẽ quay trở lại qua mỗi "cổng" và tìm kiếm phạm vi đó.

Trong ví dụ của bạn phương pháp baz được định nghĩa là

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

Vì vậy, khi cố gắng để xác định giá trị của FOO, đầu tiên lớp Bar được kiểm tra, và kể từ Bar không chứa một FOO việc tìm kiếm di chuyển lên thông qua cổng “class Bar” vào mô-đun Foo là phạm vi chứa. Foo không chứa hằng số FOO (555) vì vậy đây là kết quả bạn thấy.

Phương pháp glorf được định nghĩa là:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

Đây là “cửa ngõ” là class Foo::Bar, vì vậy khi FOO không được tìm thấy bên trong Bar các “cửa ngõ” đi qua các mô-đun Foo và thẳng vào cấp cao nhất , trong đó có một số khác là FOO (123) là những gì được hiển thị.

Lưu ý cách sử dụng class Foo::Bar tạo ra một đĩa đơn “cửa ngõ”, bỏ qua trên phạm vi Foo, nhưng module Foo; class Bar ... mở hai “cổng” riêng

+3

Btw. thời hạn cổng. Trong các nguồn Ruby có vẻ là những gì người ta có thể gọi là một "phạm vi ngăn xếp". Vì vậy, mỗi khi bạn gõ 'class' hoặc' module', một phạm vi mới sẽ được đẩy lên ngăn xếp này. Khi Ruby tìm kiếm các biến hoặc hằng số, nó sẽ truy vấn stack này từ dưới lên trên, kết thúc bằng 'main' cấp cao nhất nếu nó không tìm thấy biến trên đường lên. Trong trường hợp của 'class Foo :: Bar' nó thực sự nên đẩy HAI phạm vi lên stack (cả' Foo' và 'Bar'), nhưng nó chỉ đẩy một, do đó chúng tôi nhận được" vấn đề ". – Casper

+0

Điều này khác với câu trả lời gốc như thế nào? – rainkinz

+1

@Casper có ý nghĩa. Tôi đã đọc về ý tưởng “cổng” ở đâu đó (không thể nhớ nơi không may) như một cách suy nghĩ về những gì đang xảy ra nhưng tôi đã không xem xét việc triển khai. Tôi đoán một lời giải thích cho hành vi này là nó cho phép bạn mở một lớp lồng nhau (để khỉ vá nó) mà không cần phải lo lắng về phạm vi bao quanh can thiệp. – matt

5

wow, câu hỏi hay. Câu trả lời hay nhất tôi có thể đưa ra là trong trường hợp này, bạn đang sử dụng mô đun để xác định một không gian tên.

Kiểm tra này ra:

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 

Vì vậy, suy nghĩ của tôi là khi bạn xác định:

module Foo 
    FOO = 555 
end 

bạn đang tạo FOO trong không gian tên của Foo. Vì vậy, khi bạn sử dụng nó ở đây:

module Foo 
    class Bar 
    def baz 
     puts FOO 
    end 
    end 
end 

bạn đang ở trong không gian tên Foo. Tuy nhiên, khi bạn đề cập đến nó trong:

class Foo::Bar 
    def glorf 
    puts FOO 
    end 
end 

FOO đến từ không gian tên mặc định (như minh họa bởi ::FOO).

+0

cảm ơn các ví dụ! điều này có ý nghĩa ngoại trừ một điểm: khi bạn xác định lớp Foo :: Bar, bạn có đang đặt tên nó là Foo không? Không phải phần "Foo ::" của "Foo :: Bar" ngụ ý rằng bạn đang đặt tên cho lớp đó? – wmock

+0

Foo :: Bar.new.glorf trở về 123 rất khó để tôi hiểu khi Foo :: Bar.new.baz trả về 555. – wmock

+1

Tôi cũng đã nghĩ như vậy, nhưng có vẻ như, những gì đang xảy ra là bạn đang đặt tên thanh (theo Foo) rõ ràng bởi Foo :: Bar và mọi thứ khác trong ngữ cảnh đó vẫn đến từ không gian tên mặc định. – rainkinz

0

cuộc gọi đầu tiên:

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

in kết quả của cách gọi phương pháp baz của một thể hiện của lớp Foo :: Bar

thông báo rằng Foo :: Bar # baz định nghĩa thực sự là một đóng cửa trên FOO.quy tắc phạm vi sau đây của ruby:

  1. FOO được tìm kiếm trong Foo :: Bar (lớp, không phải là ví dụ) phạm vi, nó không được tìm thấy,
  2. FOO được tìm kiếm trong kèm theo phạm vi Foo (vì chúng tôi nằm trong định nghĩa mô-đun) và nó được tìm thấy ở đó (555)

cuộc gọi thứ hai:

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

in kết quả của cách gọi phương pháp glorf của một thể hiện của lớp Foo :: Bar

thông báo rằng Foo :: Bar # glorf định nghĩa thời gian này cũng là một đóng cửa trên FOO, nhưng nếu chúng tôi tuân theo quy tắc phạm vi của Ruby, bạn sẽ nhận thấy rằng giá trị đóng vào thời điểm này là :: FOO (phạm vi cấp cao nhất FOO) theo cách sau:

  1. FOO được tìm kiếm trong Foo :: Bar (lớp, không phải là ví dụ) namespace, nó không được tìm thấy
  2. FOO được tìm kiếm trong phạm vi kèm theo ('cấp cao nhất') và nó là tìm thấy ở đó (123)
0

glorf là ​​một phương pháp của lớp Foo, trong => [Foo, Module, Object, Kernel, BasicObject]

trong đó phạm vi (tức là trong default/mô-đun chính), FOO được gán 123

mô-đun Foo được định nghĩa là

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

nơi phương pháp baz thuộc về lớp Bar trong mô-đun Foo => [Bar, Foo, Object, Kernel, BasicObject]

và trong phạm vi FOO được giao 555

+0

'chính' được đề cập ở trên là một tạo phẩm của irb, thực sự 'FOO = 123' là một phương thức cấp cao nhất đi vào lớp Object. – aug2uag