Якщо в моєму сценарії є принаймні два екземпляри одного і того ж рядка, чи повинен я замість цього використовувати символ?
Якщо в моєму сценарії є принаймні два екземпляри одного і того ж рядка, чи повинен я замість цього використовувати символ?
Відповіді:
Просте правило - використовувати символи кожен раз, коли вам потрібні внутрішні ідентифікатори. Для Ruby <2.2 використовуйте символи лише тоді, коли вони не створюються динамічно, щоб уникнути витоку пам'яті.
Єдина причина, щоб не використовувати їх для ідентифікаторів, що генеруються динамічно, - через проблеми з пам'яттю.
Це питання дуже поширене, оскільки багато мов програмування не мають символів, а лише рядки, і таким чином рядки також використовуються як ідентифікатори у вашому коді. Ви повинні турбуватися про те, якими символами призначено бути , а не лише тоді, коли ви повинні використовувати символи . Символи мають на увазі ідентифікатори. Якщо ви будете дотримуватися цієї філософії, шанси на те, що ви зробите все правильно.
Існує кілька відмінностей між реалізацією символів і рядків. Найважливіше в символах - це те, що вони незмінні . Це означає, що у них ніколи не буде змінено їх значення. Через це символи інстанціюються швидше, ніж рядки, а деякі операції, такі як порівняння двох символів, також проходять швидше.
Те, що символ є незмінним, дозволяє Рубі використовувати один і той же об'єкт кожного разу, коли ви посилаєтесь на символ, економлячи пам'ять. Тому кожен раз, коли перекладач читає, :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 представив збірник сміття символів , який звільняє символи, що генеруються динамічно, тому пам'ять, що витікає з динамічного створення символів, вже не викликає занепокоєння.
Відповідь на ваше запитання:
Чи правда, що я повинен використовувати символ замість рядка, якщо в моєму додатку чи сценарії є принаймні дві однакові рядки?
Якщо ви шукаєте ідентифікатор, який потрібно використовувати внутрішньо у вашому коді, вам слід використовувати символи. Якщо ви друкуєте вихід, вам слід рухатись із рядками, навіть якщо вони з’являються не один раз, навіть виділяючи в пам'яті два різних об’єкти.
Ось міркування:
@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, щоб побачити, але якщо ви продовжуєте використовувати символи в концепції ідентифікатора, ваша програма буде швидшою та надійною. Коли питання вражає, порівняйте його та отримайте свої відповіді.
Простіше кажучи, символ - це ім'я, складене з символів, але незмінне. Рядок, навпаки, - це упорядкований контейнер для символів, вміст яких дозволено змінювати.
Для порівняння двох рядків нам потенційно потрібно переглянути кожен символ. Для двох рядків довжиною 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) time").
def symbol_comp sym1, sym2
sym1.object_id == sym2.object_id
end
symbol_comp :foo, :foo
У C ++ ми можемо використовувати "перерахування" для представлення родин споріднених констант:
enum BugStatus { OPEN, CLOSED };
BugStatus original_status = OPEN;
BugStatus current_status = CLOSED;
Але оскільки Ruby - це динамічна мова, ми не переживаємо про те, щоб оголосити тип BugStatus або не відслідковувати юридичні значення. Натомість ми представляємо значення перерахування як символи:
original_status = :open
current_status = :closed
3.А символ Ruby - це постійна унікальна назва
У Ruby ми можемо змінити вміст рядка:
"foo"[0] = ?b # "boo"
Але ми не можемо змінити вміст символу:
:foo[0] = ?b # Raises an error
Передаючи аргументи ключових слів функції Ruby, ми визначаємо ключові слова за допомогою символів:
# Build a URL for 'bug' using Rails.
url_for :controller => 'bug',
:action => 'show',
:id => bug.id
Зазвичай ми будемо використовувати символи для представлення ключів хеш-таблиці:
options = {}
options[:auto_save] = true
options[:show_comments] = false
Ось хороший орієнтир рядків проти символів, який я знайшов у кодексі:
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.
використовувати символи як ідентифікатори хеш-ключів
{key: "value"}
символи дозволяють викликати метод в іншому порядку
def write (файл :, data :, режим: "ascii") # видалено для стислості кінець написати (дані: 123, файл: "test.txt")
заморозити, щоб зберегти як рядок і зберегти пам'ять
label = 'My Label'.freeze
/
(післяstrings
) із посилання. Ось це: www.reactive.io/tips/2009/01/11/the-difference-bet