Ruby Koans: ทำไมต้องแปลงรายการสัญลักษณ์เป็นสตริง


86

ฉันอ้างถึงการทดสอบนี้ใน about_symbols.rb ใน 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?

ทำไมเราต้องแปลงรายการนั้นเป็นสตริงก่อน?

คำตอบ:


112

สิ่งนี้เกี่ยวข้องกับการทำงานของสัญลักษณ์ สำหรับแต่ละสัญลักษณ์มีเพียงสัญลักษณ์เดียวเท่านั้น เบื้องหลังสัญลักษณ์เป็นเพียงตัวเลขที่ชื่อเรียก (เริ่มต้นด้วยเครื่องหมายจุดคู่) ดังนั้นเมื่อเปรียบเทียบความเท่าเทียมกันของสัญลักษณ์สองตัวคุณกำลังเปรียบเทียบเอกลักษณ์ของวัตถุไม่ใช่เนื้อหาของตัวระบุที่อ้างถึงสัญลักษณ์นี้

หากคุณต้องทำการทดสอบอย่างง่าย: test == "test"จะเป็นเท็จ ดังนั้นหากคุณจะรวบรวมสัญลักษณ์ทั้งหมดที่กำหนดไว้ในอาร์เรย์คุณจะต้องแปลงเป็นสตริงก่อนจึงจะเปรียบเทียบได้ คุณไม่สามารถทำในทางตรงข้ามได้ (แปลงสตริงที่คุณต้องการเปรียบเทียบเป็นสัญลักษณ์ก่อน) เนื่องจากการทำเช่นนั้นจะสร้างอินสแตนซ์เดียวของสัญลักษณ์นั้นและ "ก่อมลพิษ" ให้รายการของคุณมีสัญลักษณ์ที่คุณกำลังทดสอบการดำรงอยู่

หวังว่าจะช่วยได้ นี่เป็นเรื่องแปลกเล็กน้อยเพราะคุณต้องทดสอบการมีสัญลักษณ์โดยไม่ได้ตั้งใจสร้างสัญลักษณ์นั้นในระหว่างการทดสอบ คุณมักจะไม่เห็นรหัสแบบนั้น


2
โปรดทราบว่าวิธีที่ดีกว่าในการดำเนินการนี้อย่างปลอดภัยคือการกำหนดผลลัพธ์ของSymbol.all_symbolsให้กับตัวแปรจากนั้นทดสอบการรวม สัญลักษณ์จะเร็วกว่าในการเปรียบเทียบและคุณหลีกเลี่ยงการแปลงสัญลักษณ์นับพันเป็นสตริง
coreyward

4
ที่ยังมีปัญหาในการสร้างสัญลักษณ์ที่ไม่สามารถทำลายได้ การทดสอบในอนาคตสำหรับสัญลักษณ์นั้นจะถูกทำลาย แต่นี่เป็นเพียงโคอันไม่จำเป็นต้องมีเหตุผลมากหรือรวดเร็วเพียงแค่แสดงให้เห็นว่าสัญลักษณ์ทำงานอย่างไร
AboutRuby

2
คำตอบนี้ใช้ไม่ได้กับฉัน หากเรากำลังค้นหาการมีอยู่ของสัญลักษณ์เหตุใดเราจึงระบุอาร์กิวเมนต์สตริงเป็นinclude?ถ้าเราระบุ:test_method_names_become_symbolsเราจะไม่ต้องแปลงสัญลักษณ์เหล่านั้นทั้งหมดเป็นสตริง
Isaac Rabinovitch

3
อิสอัคเนื่องจากปัญหาที่ระบุซึ่งก็คือการระบุ:test_method_names_become_symbolsในการเปรียบเทียบจะสร้างมันขึ้นมาดังนั้นการเปรียบเทียบจะเป็นจริงเสมอ การแปลงall_symbolsเป็นสตริงและเปรียบเทียบสตริงทำให้เราสามารถแยกแยะได้ว่าสัญลักษณ์นั้นมีอยู่ก่อนการเปรียบเทียบหรือไม่
Stephen

3
มากฉันไม่เข้าใจ ถ้าฉันทำ >> array_of_symbols = Symbol.all_symbols แล้ว >> array_of_symbols.include? (: not_yet_used) ฉันจะได้รับเท็จและฉันได้ค่าจริงสำหรับบางสิ่งที่กำหนดไว้ดังนั้นฉันจึงไม่เข้าใจว่าเหตุใดจึงจำเป็นต้องมีการแปลงสตริงเป็น .
codenoob

75

เพราะถ้าคุณทำ:

assert_equal true, all_symbols.include?(:test_method_names_become_symbols)

อาจ (ขึ้นอยู่กับการใช้งานทับทิมของคุณ) โดยอัตโนมัติเนื่องจาก:test_method_names_become_symbolsสร้างสัญลักษณ์ ดูรายงานข้อบกพร่องนี้


1
ดังนั้นคำตอบที่ยอมรับคือคำตอบที่ผิด (หรือดูเหมือนว่าฉัน)
Isaac Rabinovitch

3
Isaac คำตอบอื่นไม่ผิด แต่ไม่ได้อธิบายอย่างกระชับ แน่นอน. อย่างไรก็ตามคุณสามารถตรวจสอบสิ่งที่ Andrew พูดด้วยสิ่งต่อไปนี้: assert_equal true, Symbol.all_symbols.include? (: abcdef) สิ่งนี้จะผ่านไป (อย่างน้อยก็สำหรับฉัน) โดยไม่คำนึงถึงสัญลักษณ์ ฉันเดาว่าบทเรียนหนึ่งคืออย่าพยายามใช้การมี / ไม่มีสัญลักษณ์เป็นธงบูลีน
Stephen

1
เมื่อมองไปที่คำถามนี้ซึ่งยังคงสนใจฉัน 2 ปีต่อมาฉันซาบซึ้งกับคำตอบนี้และความคิดเห็น ฉันคิดว่า Isaac ถูกต้องนี่เป็นคำตอบที่อธิบายได้ชัดเจนที่สุดว่าเหตุใดการแปลงเป็นสตริงจึงอาจเป็นหนทางไปได้แม้ว่าฉันคิดว่าคุณสามารถวางขั้นตอนตัวกลาง (เก็บสัญลักษณ์ทั้งหมดไว้ก่อนการเปรียบเทียบ) เพื่อให้ทำงานได้ดีขึ้น
codenoob

4

คำตอบทั้งสองข้างต้นถูกต้อง แต่จากคำถามของ Karthik ข้างต้นฉันคิดว่าฉันจะโพสต์การทดสอบที่แสดงให้เห็นว่าคำตอบหนึ่งอาจส่งสัญลักษณ์ไปยัง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

หมายเหตุเพิ่มเติมเกี่ยวกับ Koans: ใช้ประโยชน์จากputsข้อความเช่นเดียวกับการทดสอบที่กำหนดเองหากคุณไม่เข้าใจอะไรเลย ตัวอย่างเช่นหากคุณเห็น:

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

และไม่รู้ว่าwordsจะเกิดอะไรขึ้นให้เพิ่มบรรทัด

puts words

และเรียกใช้rakeที่บรรทัดคำสั่ง ในทำนองเดียวกันการทดสอบเช่นเดียวกับที่ฉันได้เพิ่มไว้ข้างต้นจะมีประโยชน์ในแง่ของการทำความเข้าใจความแตกต่างบางประการของ Ruby


เมื่อมองไปที่คำถามนี้ซึ่งยังคงสนใจฉัน 2 ปีต่อมาฉันซาบซึ้งกับคำตอบนี้และความคิดเห็น
codenoob
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.