2012-06-22 24 views
15

Rõ ràng ||= sẽ không làm việcLàm thế nào tôi có thể ghi nhớ một phương thức có thể trả về true, false hoặc nil trong Ruby?

def x? 
    @x_query ||= expensive_way_to_calculate_x 
end 

bởi vì nếu nó hóa ra là false hoặc nil, sau đó expensive_way_to_calculate_x sẽ được chạy hơn và hơn.

Hiện nay cách tốt nhất mà tôi biết là đưa giá trị vào một Array:

def x? 
    return @x_query.first if @x_query.is_a?(Array) 
    @x_query = [expensive_way_to_calculate_x] 
    @x_query.first 
end 

Có một cách thông thường nhiều hơn hoặc hiệu quả để làm điều này?

CẬP NHẬT tôi nhận ra rằng tôi muốn memoize nil ngoài false - điều này đi tất cả các cách trở lại https://rails.lighthouseapp.com/projects/8994/tickets/1830-railscachefetch-does-not-work-with-false-boolean-as-cached-value - lời xin lỗi của tôi để Andrew Marshall người đã cho một câu trả lời khác hoàn toàn chính xác.

+0

Đây là những gì 'nil' và toàn bộ khái niệm của' null' là cho. – meagar

+0

Tôi đã chỉnh sửa câu hỏi của mình bởi vì tôi đã không làm điều đó ngay lần đầu tiên - tôi cũng muốn ghi nhớ nil. –

Trả lời

26

Rõ ràng kiểm tra nếu giá trị của @x_querynil thay vì:

def x? 
    @x_query = expensive_way_to_calculate_x if @x_query.nil? 
    @x_query 
end 

Lưu ý rằng nếu điều này không phải là một biến Ví dụ, bạn sẽ phải kiểm tra xem nó đã được xác định cũng/thay vào đó, kể từ khi tất cả các biến dụ mặc định là nil.

Với bản cập nhật của bạn mà @x_query 'giá trị memoized s có thể nil, bạn có thể sử dụng defined? thay vì để có được xung quanh thực tế là tất cả các biến Ví dụ mặc định để nil:

def x? 
    defined?(@x_query) or @x_query = expensive_way_to_calculate_x 
    @x_query 
end 

Lưu ý rằng làm một cái gì đó giống như a = 42 unless defined?(a) won' hoạt động như mong đợi kể từ khi trình phân tích cú pháp chạm vào a =, a được xác định trước nó đạt đến điều kiện. Tuy nhiên, điều này không đúng với các biến mẫu vì chúng mặc định là nil trình phân tích cú pháp không xác định chúng khi nó truy cập =. Bất kể, tôi nghĩ đó là một thành ngữ tốt để sử dụng hình thức chặn dài của or hoặc unless thay vì một dòng unless với defined? để duy trì sự nhất quán.

+0

Tôi sẽ upvote này bởi vì nó là câu trả lời cho phiên bản đầu tiên của câu hỏi của tôi, nhưng xin hãy xem các chỉnh sửa của tôi - một lần nữa, xin lỗi. –

+0

@SeamusAbshere Tôi đã cập nhật câu trả lời của tôi để phản ánh cập nhật của bạn ':)'. –

+3

'a = 42 trừ khi được xác định? (A)' làm cho 'a' nil, nhưng' @a = 42 trừ khi được xác định? (@ A) 'làm' @ a' 42, do đó cú pháp 'hoặc' không hoàn toàn bắt buộc trong ví dụ này. –

18

Để giải thích cho nil, sử dụng defined? để xem nếu biến đã được định nghĩa:

def x? 
    return @x_query if defined? @x_query 
    @x_query = expensive_way_to_calculate_x 
end 

defined? sẽ trở lại nil nếu biến chưa được xác định, hoặc chuỗi "instance_variable" khác:

irb(main):001:0> defined? @x 
=> nil 
irb(main):002:0> @x = 3 
=> 3 
irb(main):003:0> defined? @x 
=> "instance-variable"