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

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

Ответ 1

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

Отвечая на ваш вопрос:

Правда ли, что я должен использовать символ вместо строки, если в моем приложении есть по крайней мере две одинаковые строки или script?

Если вы ищете идентификатор, который будет использоваться внутри вашего кода, вы должны использовать символы. Если вы печатаете вывод, вы должны идти со строками, даже если он появляется более одного раза, даже выделяя два разных объекта в памяти.

Здесь рассуждения:

  • Печать символов будет медленнее, чем печать строк, потому что они передаются в строки.
  • Наличие большого количества разных символов увеличит общее использование памяти вашего приложения, поскольку они никогда не будут освобождены. И вы никогда не используете все строки из своего кода одновременно.

Использовать регистр @AlanDert

@AlanDert: если я использую много раз что-то вроде% input {type:: checkbox} в haml-коде, что я должен использовать как флажок?

Me: Да.

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

Ответ 2

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

Ответ 3

Вот хорошая строка, сравниваемая с символами, найденная мной в 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.

Ответ 4

  • Символ Ruby - это объект с сопоставлением O (1)

Чтобы сравнить две строки, нам, возможно, нужно посмотреть на каждого персонажа. Для двух строк длины N для этого потребуются сравнения N + 1 (которые компьютерные ученые называют "временем O (N)" ).

def string_comp str1, str2
  return false if str1.length != str2.length
  for i in 0...str1.length
    return false if str1[i] != str2[i]
  end
  return true
end
string_comp "foo", "foo"

Но так как каждый вид: foo относится к одному и тому же объекту, мы можем сравнивать символы, просматривая идентификаторы объектов. Мы можем сделать это с помощью одного сравнения (которые компьютерные ученые называют "O (1) раз" ).

def symbol_comp sym1, sym2
  sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
  1. Символ Ruby является меткой в ​​перечислении бесплатной формы

В С++ мы можем использовать "перечисления" для представления семейств связанных констант:

enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status  = CLOSED;

Но поскольку Ruby является динамическим языком, мы не беспокоимся об объявлении типа BugStatus или отслеживании правовых значений. Вместо этого мы представляем значения перечисления в виде символов:

original_status = :open
current_status  = :closed

3. Рубиновый символ - это константа, уникальное имя

В Ruby мы можем изменить содержимое строки:

"foo"[0] = ?b # "boo"

Но мы не можем изменить содержимое символа:

:foo[0]  = ?b # Raises an error
  1. Символ Ruby - это ключевое слово для аргумента ключевого слова

При передаче аргументов ключевого слова функции Ruby мы определяем ключевые слова с использованием символов:

# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
        :action => 'show',
        :id => bug.id
  1. Символ Ruby - отличный выбор для хеш-ключа

Обычно мы будем использовать символы для представления ключей хэш-таблицы:

options = {}
options[:auto_save]     = true
options[:show_comments] = false