2011-01-13 43 views
70

tôi đề cập đến xét nghiệm này trong about_symbols.rb trong Ruby công án https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26của Ruby công án: Tại sao chuyển đổi danh sách các biểu tượng thành các chuỗi

def test_method_names_become_symbols 
    symbols_as_strings = Symbol.all_symbols.map { |x| x.to_s } 
    assert_equal true, symbols_as_strings.include?("test_method_names_become_symbols") 
end 


    # THINK ABOUT IT: 
    # 
    # Why do we convert the list of symbols to strings and then compare 
    # against the string value rather than against symbols? 

Tại sao chính xác làm chúng ta phải chuyển đổi danh sách đó thành chuỗi đầu tiên?

Trả lời

93

Điều này liên quan đến cách biểu tượng hoạt động. Đối với mỗi biểu tượng, chỉ một biểu tượng thực sự tồn tại. Phía sau hậu trường, một biểu tượng chỉ là một con số được gọi bằng tên (bắt đầu bằng dấu hai chấm). Do đó, khi so sánh sự bình đẳng của hai biểu tượng, bạn đang so sánh danh tính đối tượng và không phải là nội dung của từ định danh đề cập đến biểu tượng này.

Nếu bạn làm bài kiểm tra đơn giản : test == "test", nó sẽ là false. Vì vậy, nếu bạn đã thu thập tất cả các biểu tượng được xác định từ trước đến một mảng, bạn sẽ cần phải chuyển đổi chúng thành chuỗi trước khi so sánh chúng. Bạn không thể làm điều này ngược lại (chuyển đổi chuỗi bạn muốn so sánh thành một biểu tượng đầu tiên) bởi vì làm điều đó sẽ tạo ra một cá thể duy nhất của biểu tượng đó và "gây ô nhiễm" danh sách của bạn với biểu tượng bạn đang thử nghiệm cho sự tồn tại.

Hy vọng điều đó sẽ hữu ích. Đây là một chút kỳ quặc, bởi vì bạn phải kiểm tra sự hiện diện của một biểu tượng mà không vô tình tạo ra biểu tượng đó trong quá trình thử nghiệm. Bạn thường không thấy mã như thế.

+1

Lưu ý rằng cách tốt hơn để làm điều này một cách an toàn là gán đầu ra của 'Symbol.all_symbols' cho một biến, sau đó kiểm tra để đưa vào. Các ký hiệu nhanh hơn khi so sánh và bạn đang tránh chuyển đổi hàng nghìn ký hiệu thành chuỗi. – coreyward

+4

Điều đó vẫn còn có vấn đề tạo ra các biểu tượng mà không thể bị phá hủy. Bất kỳ thử nghiệm nào trong tương lai cho biểu tượng đó sẽ bị hủy hoại. Nhưng đây chỉ là một Koan, nó không phải có ý nghĩa hay nhanh chóng, chỉ chứng minh làm thế nào biểu tượng làm việc. – AboutRuby

+1

Câu trả lời này không phù hợp với tôi. Nếu chúng ta đang tìm kiếm sự tồn tại của một biểu tượng, tại sao chúng ta chỉ định một đối số chuỗi cho '' include? '' Nếu chúng ta chỉ định '': test_method_names_become_symbols'', chúng ta sẽ không phải chuyển đổi tất cả các ký tự đó thành chuỗi. –

68

Bởi vì nếu bạn làm

assert_equal true, all_symbols.include?(:test_method_names_become_symbols) 

nó có thể (tùy theo tình hình thực hiện ruby ​​của bạn) tự động là đúng, bởi vì hỏi về :test_method_names_become_symbols tạo ra nó. Xem this bug report.

+1

Vì vậy, câu trả lời được chấp nhận là sai (hoặc có vẻ như với tôi). –

+3

Isaac, câu trả lời khác không sai, nhưng nó không được giải thích một cách chính xác. chắc chắn. Dù sao, bạn có thể xác minh những gì Andrew nói với những điều sau đây: assert_equal true, Symbol.all_symbols.include? (: Abcdef) Điều này sẽ luôn luôn vượt qua (ít nhất, nó cho tôi) bất kể biểu tượng. Tôi đoán một bài học là, không cố gắng sử dụng sự hiện diện/vắng mặt của các biểu tượng như một cờ boolean. – Stephen

+1

Nhìn vào câu hỏi này, điều mà tôi vẫn quan tâm, 2 năm sau, tôi thực sự đánh giá cao câu trả lời này và những bình luận. Tôi nghĩ Isaac là đúng, đây là câu trả lời rõ ràng nhất giải thích tại sao việc chuyển đổi thành chuỗi có thể là con đường để đi, mặc dù tôi nghĩ bạn có thể đặt bước trung gian (lưu trữ tất cả các ký hiệu trước khi so sánh) để làm cho nó hoạt động tốt hơn. – codenoob

3

Cả hai câu trả lời trên là đúng, nhưng trong bối cảnh của câu hỏi Karthik của trên, chúng tôi nghĩ tôi sẽ gửi một bài kiểm tra đó minh họa cách người ta có thể vượt qua một cách chính xác một biểu tượng cho phương thức include

def test_you_create_a_new_symbol_in_the_test 
    array_of_symbols = [] 
    array_of_symbols << Symbol.all_symbols 
    all_symbols = Symbol.all_symbols.map {|x| x} 
    assert_equal false, array_of_symbols.include?(:this_should_not_be_in_the_symbols_collection) #this works because we stored all symbols in an array before creating the symbol :this_should_not_be_in_the_symbols_collection in the test 
    assert_equal true, all_symbols.include?(:this_also_should_not_be_in_the_symbols_collection) #This is the case noted in previous answers...here we've created a new symbol (:this_also_should_not_be_in_the_symbols_collection) in the test and then mapped all the symbols for comparison. Since we created the symbol before querying all_symbols, this test passes. 
end 

Một lưu ý bổ sung về công án : sử dụng các câu lệnh puts cũng như các bài kiểm tra tùy chỉnh nếu bạn không hiểu bất kỳ điều gì. Ví dụ, nếu bạn thấy:

string = "the:rain:in:spain" 
words = string.split(/:/) 

và không có ý tưởng gì words có thể, hãy thêm dòng

puts words 

và chạy rake tại dòng lệnh. Tương tự như vậy, các bài kiểm tra như tôi đã thêm ở trên có thể hữu ích trong việc hiểu một số sắc thái của Ruby.

+0

Nhìn vào câu hỏi này, điều mà tôi vẫn quan tâm, 2 năm sau, tôi thực sự đánh giá cao câu trả lời này và các bình luận. – codenoob

Các vấn đề liên quan