Навіщо використовувати символи як хеш-ключі в Ruby?


162

Багато разів люди використовують символи як ключі в хеші Рубі.

Яка перевага перед використанням рядка?

Наприклад:

hash[:name]

vs.

hash['name']

Відповіді:


227

TL; DR:

Використання символів не тільки економить час при порівнянні, але й економить пам’ять, оскільки вони зберігаються лише один раз.

Символи Ruby незмінні (неможливо змінити), що робить пошук щось набагато простіше

Коротка (іш) відповідь:

Використання символів не тільки економить час при порівнянні, але й економить пам’ять, оскільки вони зберігаються лише один раз.

Символи в Ruby - це в основному "незмінні струни" . Це означає, що їх неможливо змінити, і це означає, що той самий символ, коли посилається багато разів у вашому вихідному коді, завжди зберігається як одна і та ж сутність, наприклад, має той самий ідентифікатор об'єкта .

Струни з іншого боку змінні , їх можна змінити в будь-який час. Це означає, що Ruby потрібно зберігати кожну рядок, яку ви згадуєте, у своєму вихідному коді, в її окремому об'єкті, наприклад, якщо у вас є рядок "ім'я" кілька разів, зазначений у вихідному коді, Ruby потрібно зберігати все це в окремих об'єктах String, оскільки вони може змінитися згодом (така природа рядка Ruby).

Якщо ви використовуєте рядок як ключ Hash, Ruby потрібно оцінити рядок і переглянути його вміст (і обчислити хеш-функцію на цьому) і порівняти результат із значеннями (хешованих) ключів, які вже зберігаються в Hash .

Якщо ви використовуєте символ як ключ Hash, то це неявно, що він непорушний, тому Ruby в основному може просто порівняти (хеш-функцію) об'єкта-ідентифікатора з (хешованими) об'єктами-ідентифікаторами ключів, які вже зберігаються в Хеш. (набагато швидше)

Знизу: кожен символ займає проріз у таблиці символів перекладача Ruby, який ніколи не випускається. Символи ніколи не збирають сміття. Отже, у вас є велика кількість символів (наприклад, автоматично створених символів). У цьому випадку слід оцінити, як це впливає на розмір Вашого перекладача Ruby.

Примітки:

Якщо ви робите рядкові порівняння, Ruby може порівнювати символи лише за їх ідентифікаторами, не оцінюючи їх. Це набагато швидше, ніж порівняння рядків, які потрібно оцінити.

Якщо ви отримуєте доступ до хешу, Ruby завжди застосовує хеш-функцію для обчислення "хеш-ключа" з будь-якого ключа, який ви використовуєте. Ви можете уявити щось на зразок хешу MD5. А потім Рубі порівнює ці "хешивані ключі" один проти одного.

Довга відповідь:

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, Symbols будуть GCd у наступній версії Ruby: bugs.ruby-lang.org/isissue/9634
Ajedi32

2
Також рядки автоматично заморожуються при використанні в якості клавіш Hash в Ruby. Тож не зовсім вірно, що струни є змінними, коли про них говорять у цьому контексті.
Ajedi32

1
Відмінна інформація про тему та перше посилання в розділі "Довга відповідь" видалено або перенесено.
Hbksagar

2
Символи - сміття, зібране в Ruby 2.2
Marc-André Lafortune

2
Чудова відповідь! З боку тролінгу ваша "коротка відповідь" також досить довга. ;)
технофіл

22

Причиною є ефективність, що має кілька виграшів над рядком:

  1. Символи незмінні, тому питання "що станеться, якщо ключ зміниться?" не потрібно просити.
  2. Рядки дублюються у вашому коді і зазвичай займають більше місця в пам'яті.
  3. Пошук хеш-книг повинен обчислити хеш ключів для їх порівняння. Це O(n)для рядків і константи для символів.

Більше того, Ruby 1.9 представив спрощений синтаксис лише для хешів із клавішами символів (наприклад h.merge(foo: 42, bar: 6)), а Ruby 2.0 має аргументи ключових слів, які працюють лише для символьних клавіш.

Примітки :

1) Ви можете бути здивовані, дізнавшись, що Рубі трактує 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, оскільки безпосередньо використовувався ідентифікатор об'єкта:

: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?
Асад Моосві

7

Re: в чому перевага від використання рядка?

  • Стайлінг: його Ruby-шлях
  • (Дуже) трохи швидше значення пошуку, оскільки хешування символу еквівалентно хешированию цілого чи хеширующего рядка.

  • Недолік: в таблиці символів програми використовується слот, який ніколи не випускається.


4
+1 за згадку про те, що символ ніколи не збирається сміттям.
Vortico

символ ніколи не збирає сміття - неправда з рубіну 2.2+
eudaimonia

0

Мені було б дуже цікаво прослідкувати щодо заморожених рядків, введених у Ruby 2.x.

Коли ви маєте справу з численними рядками, що надходять із введення тексту (я думаю про параметри HTTP або корисну навантаження, наприклад, через Rack), то простіше використовувати рядки скрізь.

Коли ви маєте справу з десятками з них, але вони ніколи не змінюються (якщо вони є вашим діловим "словником"), мені подобається думати, що заморожування їх може змінити значення. Я ще не зробив жодного орієнтиру, але, мабуть, це було б наближеним до виконання символів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.