เหตุใดจึงต้องใช้สัญลักษณ์เป็นแป้นแฮชใน Ruby


162

หลายครั้งที่ผู้คนใช้สัญลักษณ์เป็นกุญแจสำคัญใน Ruby hash

ข้อดีของการใช้สตริงคืออะไร

เช่น:

hash[:name]

เมื่อเทียบกับ

hash['name']

คำตอบ:


227

TL; DR:

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

สัญลักษณ์ Ruby นั้นไม่เปลี่ยนรูป (ไม่สามารถเปลี่ยนแปลงได้) ซึ่งทำให้การค้นหาง่ายขึ้นมากขึ้น

คำตอบสั้น ๆ (ish):

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

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

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

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

หากคุณใช้สัญลักษณ์เป็นคีย์แฮชก็หมายความว่ามันไม่เปลี่ยนรูปดังนั้นโดยทั่วไปแล้วทับทิมสามารถทำการเปรียบเทียบ (ฟังก์ชันแฮชของ) object-id กับรหัสวัตถุ (hashed) ที่เก็บไว้ใน แฮช (เร็วกว่ามาก)

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

หมายเหตุ:

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

หากคุณเข้าถึงแฮช Ruby จะใช้ฟังก์ชันแฮชเพื่อคำนวณ "แฮชคีย์" จากคีย์ใด ๆ ก็ตามที่คุณใช้อยู่เสมอ คุณสามารถจินตนาการบางสิ่งบางอย่างเช่น MD5-hash จากนั้นทับทิมจะเปรียบเทียบ "คีย์ที่ถูกแฮช" กับสิ่งอื่น

คำตอบยาว:

https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings

http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/


5
Fyi สัญลักษณ์จะเป็น GCd ใน Ruby รุ่นถัดไป: bugs.ruby-lang.org/issues/9634
Ajedi32

2
นอกจากนี้สตริงจะถูกแช่แข็งโดยอัตโนมัติเมื่อใช้เป็นปุ่มแฮชใน Ruby ดังนั้นจึงไม่เป็นความจริงที่ Strings นั้นจะไม่แน่นอนเมื่อพูดถึงมันในบริบทนี้
Ajedi32

1
ข้อมูลเชิงลึกที่ยอดเยี่ยมเกี่ยวกับหัวข้อ & ลิงก์แรกในส่วน "คำตอบยาว" จะถูกลบหรือย้ายออก
Hbksagar

2
สัญลักษณ์เป็นขยะที่เก็บรวบรวมใน Ruby 2.2
Marc-André Lafortune

2
คำตอบที่ดี! ในอีกด้านหนึ่งคำตอบสั้น ๆ ของคุณก็ยาวพอเช่นกัน ;)
technophyle

22

เหตุผลก็คือประสิทธิภาพด้วยการได้รับมากกว่าหนึ่งสตริง:

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

ยิ่งกว่านั้น Ruby 1.9 ยังได้แนะนำไวยากรณ์ที่ง่ายขึ้นสำหรับการแฮชด้วยปุ่มสัญลักษณ์ (เช่นh.merge(foo: 42, bar: 6)) และ Ruby 2.0 มีอาร์กิวเมนต์ของคำหลักที่ใช้งานได้กับคีย์สัญลักษณ์เท่านั้น

หมายเหตุ :

1) คุณอาจประหลาดใจที่รู้ว่า Ruby ปฏิบัติกับStringกุญแจที่แตกต่างจากชนิดอื่น ๆ อันที่จริง:

s = "foo"
h = {}
h[s] = "bar"
s.upcase!
h.rehash   # must be called whenever a key changes!
h[s]   # => nil, not "bar"
h.keys
h.keys.first.upcase!  # => TypeError: can't modify frozen string

สำหรับคีย์สตริงเท่านั้น Ruby จะใช้สำเนาที่ถูกตรึงแทนของวัตถุเอง

2) ตัวอักษร "b", "a" และ "r" ถูกเก็บไว้เพียงครั้งเดียวสำหรับเหตุการณ์ทั้งหมด:barในโปรแกรม ก่อน Ruby 2.2 มันเป็นความคิดที่ดีที่จะสร้างใหม่อย่างต่อเนื่องSymbolsซึ่งไม่เคยนำกลับมาใช้ใหม่เนื่องจากจะอยู่ในตารางการค้นหา Symbol ทั่วโลกตลอดไป Ruby 2.2 จะเก็บขยะพวกเขาดังนั้นไม่ต้องกังวล

3) ที่จริงแล้วการคำนวณแฮชสำหรับ Symbol ไม่ได้ใช้เวลาใน Ruby 1.8.x เนื่องจาก ID วัตถุถูกใช้โดยตรง:

:bar.object_id == :bar.hash # => true in Ruby 1.8.7

ใน Ruby 1.9.x สิ่งนี้มีการเปลี่ยนแปลงเมื่อแฮชเปลี่ยนจากเซสชันหนึ่งเป็นอีกเซสชันหนึ่ง (รวมถึงเซสชันSymbols):

:bar.hash # => some number that will be different next time Ruby 1.9 is ran

+1 สำหรับบันทึกย่อที่ยอดเยี่ยมของคุณ! ฉันเดิมไม่ได้พูดถึงฟังก์ชั่นกัญชาในคำตอบของฉันเพราะฉันพยายามที่จะทำให้มันง่ายต่อการอ่าน :)
Tilo

@Tilo: นั่นคือเหตุผลที่ฉันเขียนคำตอบของฉัน :-) ฉันเพิ่งแก้ไขคำตอบของฉันเพื่อพูดถึงไวยากรณ์พิเศษใน Ruby 1.9 และพารามิเตอร์ที่มีชื่อสัญญาของ Ruby 2.0
Marc-André Lafortune

คุณช่วยอธิบายได้อย่างไรว่าการค้นหา Hash นั้นคงที่สำหรับ Symbols และ O (n) สำหรับ Strings?
ซาด Moosvi

7

Re: อะไรคือข้อดีของการใช้สตริง?

  • จัดแต่งทรงผม: มันเป็นรูบีทาง
  • (มาก) การค้นหาค่าที่เร็วขึ้นเล็กน้อยเนื่องจากการแฮ็กสัญลักษณ์นั้นเทียบเท่ากับการแปลงตัวเลขเป็นจำนวนเต็มเทียบกับการแฮชสตริง

  • ข้อเสีย: ใช้ช่องในตารางสัญลักษณ์ของโปรแกรมที่ไม่เคยเปิดตัว


4
+1 สำหรับการกล่าวถึงว่าสัญลักษณ์ไม่เคยถูกรวบรวมขยะ
Vortico

สัญลักษณ์ไม่เคยถูกเก็บรวบรวมขยะ - ไม่เป็นความจริงตั้งแต่ทับทิม 2.2+
eudaimonia

0

ฉันสนใจที่จะติดตามผลเกี่ยวกับสตริงที่ถูกตรึงใน Ruby 2.x

เมื่อคุณจัดการกับสตริงจำนวนมากที่มาจากการป้อนข้อความ (ตัวอย่างเช่นฉันกำลังคิดถึง HTTP params หรือ payload ผ่าน Rack เป็นต้น) มันง่ายกว่าที่จะใช้สตริงทุกที่

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

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