2012-05-04 30 views
7

Tôi đã theo ấn tượng rằng định nghĩa lớp trong Ruby có thể được mở cửa trở lại:biến địa phương trong định nghĩa lớp/phạm vi

class C 
    def x 
    puts 'x' 
    end 
end 

class C 
    def y 
    puts 'y' 
    end 
end 

này hoạt động như mong đợi và y được thêm vào định nghĩa lớp gốc.

Tôi đang bối rối là tại sao đoạn mã sau không hoạt động như mong đợi:

class D 
    x = 12 
end 

class D 
    puts x 
end 

này sẽ gây ra một ngoại lệ NameError. Tại sao có một phạm vi địa phương mới bắt đầu khi một lớp học được mở cửa trở lại? Điều này có vẻ hơi phản trực giác. Có cách nào để tiếp tục phạm vi địa phương trước đó khi một lớp được mở rộng không?

+0

Đây là một câu hỏi hay, nhưng có lý do bạn muốn làm điều này thay vì lưu trữ một biến cá thể trên chính đối tượng lớp (tức là thay thế 'x' bằng' @ x' ở trên)? – Phrogz

+0

@Phrogz Không, không có lý do gì tôi muốn làm điều này trong mã sản xuất. Tôi yêu cầu điều này để làm xước một sự khôn ngoan về trí tuệ thay vì giải quyết một vấn đề thực tế. – Matty

Trả lời

7

biến địa phương không được kết hợp với một đối tượng , họ đang liên kết với một phạm vi .

Biến cục bộ được sắp xếp theo ngôn từ. Những gì bạn đang cố gắng làm là không hợp lệ hơn:

def foo 
    x = :hack if false # Ensure that x is a local variable 
    p x if $set   # Won't be called the first time through 
    $set = x = 42  # Set the local variable and a global flag 
    p :done 
end 

foo     #=> :done 

foo     #=> nil (x does not have the value from before) 
         #=> done 

Ở trên cùng một phương thức, trên cùng một đối tượng, được thực thi cả hai lần. self không thay đổi. Tuy nhiên, các biến cục bộ sẽ bị xóa giữa các lời gọi.

Mở lại lớp giống như gọi lại phương thức: bạn đang ở trong cùng phạm vi self, nhưng bạn đang bắt đầu ngữ cảnh cục bộ mới. Khi bạn đóng khối class D với end, các biến cục bộ của bạn sẽ bị hủy (trừ khi chúng là closed over).

6

Trong ruby, các biến cục bộ chỉ có thể truy cập trong phạm vi được xác định. Tuy nhiên, các từ khóa class gây ra một phạm vi hoàn toàn mới hoàn toàn mới.

class D 
    # One scope 
    x = 12 
end 

class D 
    # Another scope 
    puts x 
end 

Vì vậy, bạn không thể truy cập vào biến địa phương mà quy định tại phần đầu tiên class, bởi vì khi bạn rời khỏi phạm vi đầu tiên, biến cục bộ bên trong nó bị phá hủy và nhớ được giải phóng bởi thu gom rác thải.

Điều này cũng đúng với ví dụ def.

+2

Điều này không chỉ là giải quyết câu hỏi? Có, đó là một phạm vi khác nhau, OP công nhận điều đó. Nhưng tại sao? – AShelly

+1

Cảm ơn, tôi đã thêm vào câu trả lời của mình. – Flexoid

0

Giải pháp dễ dàng là tạo x a class instance variable. Tất nhiên, điều này không trả lời câu hỏi phạm vi của bạn. Tôi tin rằng lý do x không nằm trong số scope (xem Phát hiện Phạm vi của Biến số Ruby) của lớp được mở lại là do phạm vi của nó chưa bao giờ là lớp D ngay từ đầu. Phạm vi của x kết thúc khi lớp D đóng vì x không phải là biến mẫu cũng như biến lớp.

Tôi hy vọng điều đó sẽ hữu ích.

+0

Không đúng sự thật. Hãy thử 'x = 42; lớp Foo; x = 17; p x; kết thúc; p x' và chú ý rằng bạn thấy '17' và sau đó là' 42'. Việc mở lớp sẽ bắt đầu một ngữ cảnh cục bộ mới. – Phrogz

0

Một phần lý do tại sao hành vi này có ý nghĩa với khả năng lập trình meta; bạn có thể sử dụng một số tạm biến để lưu trữ dữ liệu mà bạn có thể sử dụng để đặt tên một hằng số mới, hoặc để tham khảo một tên phương pháp:

class A 
    names, values = get_some_names_and_values() 

    names.each_with_index do |name, idx| 
    const_set name, value[idx] 
    end 
end 

Hoặc có thể, bạn cần phải nhận được eigenclass ...

class B 
    eigenclass = class << self; self; end 
    eigenclass.class_eval do 
    ... 
    end 
end 

Nó làm cho tinh thần để có một phạm vi mới mỗi khi bạn mở lại một lớp, bởi vì trong Ruby bạn thường viết mã bên trong một definiton lớp đó là mã thực tế để được thực thi và mở một lớp học chỉ là một cách để có được giá trị phù hợp cho self. Bạn không muốn nội dung của lớp bị ô nhiễm bởi các biến này, nếu bạn muốn sử dụng biến phiên bản lớp:

class C 
    @answer = 42 
end 

class C 
    p @answer 
end 
Các vấn đề liên quan