Ruby Koans: Tại sao phải chuyển đổi danh sách các biểu tượng thành chuỗi


85

Tôi đang đề cập đến bài kiểm tra này trong about_symbols.rb trong Ruby Koans https://github.com/edgecase/ruby_koans/blob/master/src/about_symbols.rb#L26

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?

Chính xác thì tại sao chúng ta phải chuyển đổi danh sách đó thành chuỗi trước?

Câu trả lời:


110

Điều này liên quan đến cách các biểu tượng hoạt động. Đối với mỗi biểu tượng, chỉ một trong số nó thực sự tồn tại. Đằng sau hậu trường, một biểu tượng chỉ là một 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 nhận dạng đối tượng chứ không phải nội dung của mã nhận dạng đề cập đến biểu tượng này.

Nếu bạn thực hiện bài kiểm tra đơn giản : test == "test" , nó sẽ là false. Vì vậy, nếu bạn tập hợp tất cả các ký hiệu được định nghĩa từ trước đến nay thành một mảng, trước tiên bạn cần 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 theo cách ngược lại (trước tiên hãy chuyển đổi chuỗi bạn muốn so sánh thành một biểu tượng) bởi vì làm điều đó sẽ tạo ra một trường hợp 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 mà bạn đang kiểm tra sự tồn tại.

Hy vọng rằng sẽ giúp. Điều này hơi 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 kiểm tra. Bạn thường không thấy mã như vậy.


2
Lưu ý rằng cách tốt hơn để thực hiện điều này một cách an toàn là gán đầu ra của Symbol.all_symbolsmộ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 vấn đề trong việc tạo ra biểu tượng không thể bị phá hủy. Mọi thử nghiệm 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 có nhiều ý nghĩa hay nhanh chóng, chỉ cần chứng minh cách các biểu tượng hoạt động.
AboutRuby

2
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 lại chỉ định đối số chuỗi thành include?Nếu chúng ta đã chỉ định, :test_method_names_become_symbolschúng ta sẽ không phải chuyển đổi tất cả các biểu tượng đó thành chuỗi.
Isaac Rabinovitch

3
Isaac, vì vấn đề đã được xác định, đó là việc chỉ định :test_method_names_become_symbolstrong phép so sánh sẽ tạo ra nó, vì vậy sự so sánh sẽ luôn đúng. Bằng cách chuyển đổi all_symbolsthành chuỗi và so sánh các chuỗi, chúng ta có thể phân biệt liệu ký hiệu có tồn tại trước khi so sánh hay không.
Stephen

3
Tôi không hiểu nhiều lắm. Nếu tôi làm >> array_of_symbols = Symbol.all_symbols thì >> array_of_symbols.include? (: Not_yet_used), tôi nhận được sai và tôi nhận được true cho một cái gì đó đã được xác định, vì vậy tôi không hiểu tại sao việc chuyển đổi thành chuỗi là cần thiết .
codenoob

75

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 thuộc vào việc triển khai ruby ​​của bạn) tự động đúng, vì :test_method_names_become_symbolstạo ra biểu tượng. Xem báo cáo lỗi này .


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

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

1
Nhìn vào câu hỏi này, mà vẫn làm tôi 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 ý kiến. Tôi nghĩ Isaac đúng, đây là câu trả lời giải thích rõ ràng nhất tại sao việc chuyển đổi thành chuỗi có thể là cách tốt nhất, 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 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 đều đúng, nhưng theo câu hỏi của Karthik ở trên, tôi nghĩ tôi sẽ đăng một bài kiểm tra minh họa cách người ta có thể chuyển chính xác một ký hiệu cho includephương thức

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ề Koans: sử dụng các putscâu lệnh cũng như các bài kiểm tra tùy chỉnh nếu bạn không hiểu gì. Ví dụ, nếu bạn thấy:

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

và không biết wordscó thể là gì , hãy thêm dòng

puts words

và chạy rakeở 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.


Nhìn vào câu hỏi này, mà vẫn làm tôi 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 ý kiến.
codenoob
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.