Ответы:
TL; DR:
Использование символов не только экономит время при выполнении сравнений, но и экономит память, поскольку они сохраняются только один раз.
Рубиновые символы являются неизменяемыми (не могут быть изменены), что значительно упрощает поиск
Краткий (иш) ответ:
Использование символов не только экономит время при выполнении сравнений, но и экономит память, поскольку они сохраняются только один раз.
Символы в Ruby в основном являются «неизменяемыми строками» .. это означает, что они не могут быть изменены, и это означает, что один и тот же символ при многократном обращении по всему исходному коду всегда сохраняется как одна и та же сущность, например, имеет один и тот же идентификатор объекта ,
С другой стороны, строки изменчивы , их можно изменить в любое время. Это подразумевает, что Ruby необходимо хранить каждую строку, которую вы упоминаете в исходном коде, в отдельной сущности, например, если у вас несколько раз упоминается строка «имя» в вашем исходном коде, Ruby необходимо хранить их все в отдельных объектах String, потому что они может измениться позже (такова природа строки Ruby).
Если вы используете строку в качестве ключа Hash, Ruby необходимо оценить строку и посмотреть ее содержимое (и вычислить хэш-функцию для этого) и сравнить результат со значениями (хэшированными) ключей, которые уже хранятся в Hash. ,
Если вы используете символ в качестве хэш-ключа, это неявно означает, что он неизменен, поэтому Ruby в основном может просто сравнить (hash function of) object-id с (hashed) object-id ключей, которые уже хранятся в Хеш (намного быстрее)
Недостаток: каждый символ занимает слот в таблице символов интерпретатора Ruby, который никогда не освобождается. Символы никогда не собираются мусором. Так что угловой случай - это когда у вас есть большое количество символов (например, автоматически сгенерированных). В этом случае вы должны оценить, как это влияет на размер вашего интерпретатора Ruby.
Ноты:
Если вы выполняете сравнение строк, Ruby может сравнивать символы только по их идентификаторам объектов, без необходимости их оценки. Это намного быстрее, чем сравнение строк, которые необходимо оценить.
Если вы обращаетесь к хешу, Ruby всегда применяет хеш-функцию для вычисления «хеш-ключа» из любого ключа, который вы используете. Вы можете представить что-то вроде MD5-хеша. И тогда Руби сравнивает эти «хешированные ключи» друг с другом.
Длинный ответ:
Причина в эффективности, с множественным усилением над строкой:
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, поскольку идентификатор объекта использовался напрямую:
: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), гораздо проще использовать строки везде.
Когда вы имеете дело с десятками из них, но они никогда не меняются (если они «словарный запас» вашего бизнеса), мне нравится думать, что их замораживание может иметь значение. Я еще не делал никаких тестов, но думаю, это будет близко к производительности символов.