Відповіді:
TL; DR:
Використання символів не тільки економить час при порівнянні, але й економить пам’ять, оскільки вони зберігаються лише один раз.
Символи Ruby незмінні (неможливо змінити), що робить пошук щось набагато простіше
Коротка (іш) відповідь:
Використання символів не тільки економить час при порівнянні, але й економить пам’ять, оскільки вони зберігаються лише один раз.
Символи в Ruby - це в основному "незмінні струни" . Це означає, що їх неможливо змінити, і це означає, що той самий символ, коли посилається багато разів у вашому вихідному коді, завжди зберігається як одна і та ж сутність, наприклад, має той самий ідентифікатор об'єкта .
Струни з іншого боку змінні , їх можна змінити в будь-який час. Це означає, що Ruby потрібно зберігати кожну рядок, яку ви згадуєте, у своєму вихідному коді, в її окремому об'єкті, наприклад, якщо у вас є рядок "ім'я" кілька разів, зазначений у вихідному коді, Ruby потрібно зберігати все це в окремих об'єктах String, оскільки вони може змінитися згодом (така природа рядка Ruby).
Якщо ви використовуєте рядок як ключ Hash, Ruby потрібно оцінити рядок і переглянути його вміст (і обчислити хеш-функцію на цьому) і порівняти результат із значеннями (хешованих) ключів, які вже зберігаються в Hash .
Якщо ви використовуєте символ як ключ Hash, то це неявно, що він непорушний, тому Ruby в основному може просто порівняти (хеш-функцію) об'єкта-ідентифікатора з (хешованими) об'єктами-ідентифікаторами ключів, які вже зберігаються в Хеш. (набагато швидше)
Знизу: кожен символ займає проріз у таблиці символів перекладача Ruby, який ніколи не випускається. Символи ніколи не збирають сміття. Отже, у вас є велика кількість символів (наприклад, автоматично створених символів). У цьому випадку слід оцінити, як це впливає на розмір Вашого перекладача Ruby.
Примітки:
Якщо ви робите рядкові порівняння, Ruby може порівнювати символи лише за їх ідентифікаторами, не оцінюючи їх. Це набагато швидше, ніж порівняння рядків, які потрібно оцінити.
Якщо ви отримуєте доступ до хешу, Ruby завжди застосовує хеш-функцію для обчислення "хеш-ключа" з будь-якого ключа, який ви використовуєте. Ви можете уявити щось на зразок хешу MD5. А потім Рубі порівнює ці "хешивані ключі" один проти одного.
Довга відповідь:
Причиною є ефективність, що має кілька виграшів над рядком:
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
Re: в чому перевага від використання рядка?
(Дуже) трохи швидше значення пошуку, оскільки хешування символу еквівалентно хешированию цілого чи хеширующего рядка.
Недолік: в таблиці символів програми використовується слот, який ніколи не випускається.
Мені було б дуже цікаво прослідкувати щодо заморожених рядків, введених у Ruby 2.x.
Коли ви маєте справу з численними рядками, що надходять із введення тексту (я думаю про параметри HTTP або корисну навантаження, наприклад, через Rack), то простіше використовувати рядки скрізь.
Коли ви маєте справу з десятками з них, але вони ніколи не змінюються (якщо вони є вашим діловим "словником"), мені подобається думати, що заморожування їх може змінити значення. Я ще не зробив жодного орієнтиру, але, мабуть, це було б наближеним до виконання символів.