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



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

555   4  

4 ответов:

TL; DR

простое эмпирическое правило заключается в использовании символов каждый раз, когда вы нужны внутренние идентификаторы. Для Ruby

ответ

единственная причина не использовать их идентификаторы, которые генерируются динамически из-за проблем с памятью.

этот вопрос очень распространен, потому что многие языки программирования не имеют символов, только строки и, следовательно, строки также используются в качестве идентификаторов в коде. Вы должны беспокоиться о том, какие символы должно быть не только когда вы должны использовать символы. Символы должны быть идентификаторами. Если вы будете следовать этой философии, скорее всего, вы будете делать все правильно.

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

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

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

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

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

Вот рассуждения:

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

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

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

  1. символ Рубина-это объект с O (1) сравнением

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

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. символ Рубина-это метка в свободной форме перечисления

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

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 мы задаем ключевые слова с помощью символов:

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

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

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

вот хороший тест strings vs symbols, который я нашел в 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.

Comments

    Ничего не найдено.