เมื่อใดควรใช้สัญลักษณ์แทนสตริงใน Ruby


คำตอบ:


177

TL; ดร

หลักการง่ายๆคือการใช้สัญลักษณ์ทุกครั้งที่คุณต้องการตัวระบุภายใน สำหรับ Ruby <2.2 ใช้เฉพาะสัญลักษณ์เมื่อไม่ได้สร้างแบบไดนามิกเพื่อหลีกเลี่ยงการรั่วไหลของหน่วยความจำ

คำตอบเต็ม

เหตุผลเดียวที่ไม่ใช้สำหรับตัวระบุที่สร้างขึ้นแบบไดนามิกเป็นเพราะปัญหาเกี่ยวกับหน่วยความจำ

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

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

ความจริงที่ว่าสัญลักษณ์ไม่เปลี่ยนรูปทำให้ Ruby สามารถใช้วัตถุเดียวกันทุกครั้งที่คุณอ้างอิงสัญลักษณ์ซึ่งช่วยประหยัดหน่วยความจำ ดังนั้นทุกครั้งที่ล่ามอ่าน:my_keyมันสามารถใช้มันจากหน่วยความจำแทนที่จะสร้างอินสแตนซ์อีกครั้ง ซึ่งมีค่าใช้จ่ายน้อยกว่าการเริ่มต้นสตริงใหม่ทุกครั้ง

คุณสามารถรับรายการสัญลักษณ์ทั้งหมดที่สร้างอินสแตนซ์ด้วยคำสั่งแล้วSymbol.all_symbols:

symbols_count = Symbol.all_symbols.count # all_symbols is an array with all 
                                         # instantiated symbols. 
a = :one
puts a.object_id
# prints 167778 

a = :two
puts a.object_id
# prints 167858

a = :one
puts a.object_id
# prints 167778 again - the same object_id from the first time!

puts Symbol.all_symbols.count - symbols_count
# prints 2, the two objects we created.

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

ตอบคำถามของคุณ:

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

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

นี่คือเหตุผล:

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

ใช้กรณีโดย @AlanDert

@AlanDert: ถ้าฉันใช้หลาย ๆ ครั้งเช่น% input {type:: checkbox} ในโค้ด haml ฉันควรใช้อะไรเป็นช่องทำเครื่องหมาย

ฉัน: ใช่

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

อินพุตคืออะไร? ตัวระบุประเภทอินพุตที่คุณต้องการใช้หรือสิ่งที่คุณต้องการแสดงต่อผู้ใช้?

เป็นความจริงที่ว่าจะกลายเป็นโค้ด HTML ในบางจุด แต่ในขณะที่คุณเขียนโค้ดบรรทัดนั้นหมายถึงการเป็นตัวระบุซึ่งจะระบุว่าคุณต้องการช่องป้อนข้อมูลประเภทใด ดังนั้นจึงใช้ซ้ำแล้วซ้ำอีกในรหัสของคุณและมี "สตริง" ของอักขระเป็นตัวระบุเสมอและจะไม่ทำให้หน่วยความจำรั่วไหล

ที่กล่าวว่าทำไมเราไม่ประเมินข้อมูลเพื่อดูว่าสตริงเร็วขึ้นหรือไม่?

นี่เป็นเกณฑ์มาตรฐานง่ายๆที่ฉันสร้างขึ้นสำหรับสิ่งนี้:

require 'benchmark'
require 'haml'

str = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: "checkbox"}').render
  end
end.total

sym = Benchmark.measure do
  10_000.times do
    Haml::Engine.new('%input{type: :checkbox}').render
  end
end.total

puts "String: " + str.to_s
puts "Symbol: " + sym.to_s

สามเอาท์พุท:

# first time
String: 5.14
Symbol: 5.07
#second
String: 5.29
Symbol: 5.050000000000001
#third
String: 4.7700000000000005
Symbol: 4.68

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


@andrewcockerham ลิงก์ที่คุณให้ไว้ใช้งานไม่ได้ (Error-404) คุณต้องลบ last /(หลังstrings) ออกจากลิงค์ นี่คือ: www.reactive.io/tips/2009/01/11/the-difference-between-ruby-‌ สัญลักษณ์และสตริง
Atul Khanduri

14

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


4
+1. Symbols and Strings เป็นสิ่งที่แตกต่างกันอย่างสิ้นเชิง ไม่มีความสับสนว่าจะใช้อันไหนเว้นแต่จะได้รับการสอนที่ไม่ดี (กล่าวคือ "สัญลักษณ์เป็นเพียงสตริงที่ไม่เปลี่ยนรูป")
Jörg W Mittag

@ JörgWMittag: แน่นอน
Boris Stitnicky

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

1
@ JörgWMittagซึ่งเกิดขึ้นทั่วทั้งเว็บเว้นแต่ว่าคุณจะดูเอกสารหรือโชคดีพอที่จะพบคนที่สนใจที่จะอธิบายสิ่งต่างๆตามที่เป็นจริง
sargas

5

นี่คือเกณฑ์มาตรฐานที่ดีของสตริงและสัญลักษณ์ที่ฉันพบใน codecademy:

require 'benchmark'

string_AZ = Hash[("a".."z").to_a.zip((1..26).to_a)]
symbol_AZ = Hash[(:a..:z).to_a.zip((1..26).to_a)]

string_time = Benchmark.realtime do
  1000_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  1000_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

ผลลัพธ์คือ:

String time: 0.21983 seconds.
Symbol time: 0.087873 seconds.

2
อย่าละสายตาจากความจริงที่ว่านี่คือหนึ่งในสิบของวินาที
Casey

เป็นญาติกันหมด บางครั้งเรื่องที่ร้อย
Yurii

2
หนึ่งในร้อยวินาทีในการทำซ้ำล้านครั้ง? หากนั่นเป็นการเพิ่มประสิทธิภาพที่ดีที่สุดสำหรับคุณโปรแกรมของคุณได้รับการปรับให้เหมาะสมแล้วฉันคิดว่า
Casey

0
  • ใช้สัญลักษณ์เป็นตัวระบุคีย์แฮช

    {key: "value"}

  • สัญลักษณ์ช่วยให้คุณสามารถเรียกใช้เมธอดในลำดับอื่นได้

     def เขียน (ไฟล์:, data:, โหมด: "ascii")
          # ถูกลบออกเพื่อความกะทัดรัด
     จบ
     เขียน (ข้อมูล: 123, ไฟล์: "test.txt")
  • ตรึงเพื่อเก็บเป็นสตริงและบันทึกหน่วยความจำ

    label = 'My Label'.freeze

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