Когда в Ruby использовать символы вместо строк?

98

Если в моем сценарии есть как минимум два экземпляра одной и той же строки, следует ли вместо этого использовать символ?

Алан Коромано
источник

Ответы:

175

TL; DR

Простое практическое правило - использовать символы каждый раз, когда вам нужны внутренние идентификаторы. Для 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

Таким образом, использование smbols на самом деле немного быстрее, чем использование строк. Это почему? Это зависит от способа реализации HAML. Мне нужно было бы немного взломать код HAML, чтобы увидеть, но если вы продолжите использовать символы в концепции идентификатора, ваше приложение будет быстрее и надежнее. Когда возникают вопросы, сравните их и получите ответы.

фотанус
источник
@andrewcockerham Указанная вами ссылка не работает (ошибка-404). Вы должны удалить последнее /(после strings) из ссылки. Вот он: www.reactive.io/tips/2009/01/11/the-difference-between-ruby-‌ символы-и-строки
Атул Хандури
14

Проще говоря, символ - это имя, состоящее из символов, но неизменное. Напротив, строка - это упорядоченный контейнер для символов, содержимое которых может изменяться.

Борис Ститницкий
источник
4
+1. Символы и строки - это совершенно разные вещи. На самом деле нет никакой путаницы относительно того, какой из них использовать, если только они не были плохо обучены (т. Е. Заблуждение «символ - это просто неизменная строка»).
Jörg W Mittag
@ JörgWMittag: Совершенно верно.
Борис Ститницкий
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
Не будем упускать из виду тот факт, что это десятые доли секунды.
Кейси
Все относительно. Иногда имеет значение сотое.
Юрий
2
Сотые доли секунды за миллион итераций? Если это лучшая из доступных вам оптимизаций, я думаю, ваша программа уже достаточно хорошо оптимизирована.
Кейси
0
  • использовать символы в качестве идентификаторов хеш-ключей

    {key: "value"}

  • символы позволяют вызывать метод в другом порядке

     def write (файл :, данные :, режим: "ascii")
          # удалено для краткости
     конец
     запись (данные: 123, файл: "test.txt")
  • заморозить, чтобы сохранить как строку и сохранить память

    label = 'My Label'.freeze

Ошан Висумерума
источник