Несмотря на чтение "Понимания Символов Ruby", я все еще смущен представлением данных в памяти когда дело доходит до использования символов. Если символ, два из них содержавшийся в различных объектах, существует в той же ячейке памяти, то как получается, что они содержат различные значения? Я ожидал бы, что та же ячейка памяти будет содержать то же значение.
Это кавычка из ссылки:
В отличие от строк, символы того же имени инициализируются и существуют в памяти только однажды во время сессии рубина
Я не понимаю, как этому удается дифференцировать значения, содержавшиеся в той же ячейке памяти.
Рассмотрите этот пример:
patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }
patient1.each_key {|key| puts key.object_id.to_s}
3918094
patient2.each_key {|key| puts key.object_id.to_s}
3918094
patient1
и patient2
оба хеши, это прекрасно. :ruby
однако символ. Если мы должны были произвести следующее:
patient1.each_key {|key| puts key.to_s}
Затем, что будет произведено? "red"
, или "programming"
?
Забывая хеши в течение секунды, я думаю, что символ является указателем на значение. Вопросы, которые я имею:
Примите во внимание следующее:
x = :sym
y = :sym
(x.__id__ == y.__id__ ) && ( :sym.__id__ == x.__id__) # => true
x = "string"
y = "string"
(x.__id__ == y.__id__ ) || ( "string".__id__ == x.__id__) # => false
Итак, как бы вы ни создавали объект символа, если его содержимое одинаково, он будет ссылаться на тот же объект в объем памяти. Это не проблема, потому что символ является неизменяемым объектом . Строки изменчивы.
(В ответ на комментарий ниже)
В исходной статье значение не сохраняется в символе, оно сохраняется в хэше. Рассмотрим это:
hash1 = { "string" => "value"}
hash2 = { "string" => "value"}
Это создает шесть объектов в памяти - четыре строковых объекта и два хэш-объекта.
hash1 = { :symbol => "value"}
hash2 = { :symbol => "value"}
Это создает только пять объектов в памяти - один символ, две строки и два хеш-объекта.
Символ : рубин
не содержит «красный»
или «программирование»
. Символ : рубин
- это просто символ : рубин
. Это ваши хэши, пациент1
и пациент2
, каждый из которых содержит эти значения, в каждом случае на которые указывает один и тот же ключ.
Подумайте об этом так: если вы войдете в гостиную в рождественское утро и увидите две коробки с биркой, на которой написано «Кеззер». У одного есть носки, а у другого - уголь. Вы не запутаетесь и не спросите, как «Кеззер» может содержать и носки, и уголь, хотя это одно и то же название. Потому что в названии нет (дерьмовых) подарков. Он просто указывает на них. Точно так же : ruby
не содержит значений в вашем хэше, он просто указывает на них.
Вы могли предположить, что сделанное вами объявление определяет значение символа как нечто иное, чем то, чем оно является. Фактически, символ - это просто «внутреннее» строковое значение, которое остается постоянным. Поскольку они хранятся с использованием простого целочисленного идентификатора, они часто используются, поскольку это более эффективно, чем управление большим количеством строк переменной длины.
Рассмотрим ваш пример:
patient1 = { :ruby => "red" }
Это следует читать так: «объявить переменную пациента1 и определить ее как хэш, и в этом хранилище значение« красный »под ключом (символ« рубин ») "
Другой способ записать это:
patient1 = Hash.new
patient1[:ruby] = 'red'
puts patient1[:ruby]
# 'red'
Когда вы выполняете задание, неудивительно, что результат, который вы получаете, идентичен тому, что вы присвоили ему с самого начала.
Концепция символа может немного сбивать с толку, поскольку она не характерна для большинства других языков.
Каждый объект String отличается, даже если значения идентичны:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
puts v.inspect + ' ' + v.object_id.to_s
end
# "foo" 2148099960
# "foo" 2148099940
# "foo" 2148099920
# "bar" 2148099900
# "bar" 2148099880
# "bar" 2148099860
Каждый символ с одинаковым значением относится к одному и тому же объекту:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
puts v.inspect + ' ' + v.object_id.to_s
end
# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668
Преобразование строк в символы сопоставляет идентичные значения с одним и тем же уникальным символом:
[ "foo", "foo", "foo", "bar", "bar", "bar" ].each do |v|
v = v.to_sym
puts v.inspect + ' ' + v.object_id.to_s
end
# :foo 228508
# :foo 228508
# :foo 228508
# :bar 228668
# :bar 228668
# :bar 228668
Аналогичным образом , преобразование из символа в строку создает каждый раз отдельную строку:
[ :foo, :foo, :foo, :bar, :bar, :bar ].each do |v|
v = v.to_s
puts v.inspect + ' ' + v.object_id.to_s
end
# "foo" 2148097820
# "foo" 2148097700
# "foo" 2148097580
# "bar" 2148097460
# "bar" 2148097340
# "bar" 2148097220
Вы можете думать о значениях символов как о взятых из внутренней хеш-таблицы, и вы можете видеть все значения, которые были закодированы в символы, используя простой вызов метода:
Symbol.all_values
# => [:RUBY_PATCHLEVEL, :vi_editing_mode, :Separator, :TkLSHFT, :one?, :setuid?, :auto_indent_mode, :setregid, :back, :Fail, :RET, :member?, :TkOp, :AP_NAME, :readbyte, :suspend_context, :oct, :store, :WNOHANG, :@seek, :autoload, :rest, :IN_INPUT, :close_read, :type, :filename_quote_characters=, ...
По мере того, как вы определяете новые символы либо с помощью двоеточия, либо с помощью .to_sym, эта таблица будет расти.
Я смог понять символы, когда подумал об этом следующим образом. Строка в Ruby - это объект, который имеет кучу методов и свойств. Люди любят использовать строки для ключей, а когда строка используется для ключа, то все эти дополнительные методы не используются. Поэтому они создали символы, которые являются строковыми объектами со всеми функциями, кроме тех, которые необходимы для того, чтобы быть хорошим ключом.
Просто думайте о символах как о постоянных строках.
Символы не являются указателями. Они не содержат значений. Символы просто - это . : рубин
- это символ : рубин
, и это все. Он не содержит значения, он ничего не делает , он просто существует как символ : ruby
. Символ : рубин
- это значение, подобное числу 1. Оно не указывает на другое значение больше, чем число 1.
пациент1.each_key {| ключ | помещает key.to_s}
Тогда что будет выводиться? "красный" или "программирование"?
Ни то, ни другое, вывод "рубин".
Вы путаете символы и хеши. Они не связаны между собой, но вместе полезны. Рассматриваемый символ : рубин
; он не имеет ничего общего со значениями в хэше, и его внутреннее целочисленное представление всегда будет одним и тем же, и его «значение» (при преобразовании в строку) всегда будет «рубиновым».
пациент1 = {: рубин => "красный"} пациент2 = {: рубин => "программирование"} пациент1.each_key {| ключ | помещает key.object_id.to_s} 3918094 Patient2.each_key {| key | помещает key.object_id.to_s} 3918094
пациент1
ипациент2
оба являются хешами, это нормально.: рубин
, однако, является символом. Если бы мы вывели следующее:Patient1.each_key {| key | помещает key.to_s}
Тогда что будет выводиться? «красный» или «программирование»?
Ни то, ни другое, конечно. На выходе будет рубин
. Что, кстати, вы могли бы узнать за меньшее время, чем вам потребовалось, чтобы ввести вопрос, просто введя его вместо этого в IRB.
Почему будет красным
или программированием
? Символы всегда оцениваются сами по себе. Значение символа : ruby
- это сам символ : ruby
, а строковое представление символа : ruby
- это строковое значение «рубин»
.
[Кстати: put
в любом случае всегда преобразует свои аргументы в строки. Нет необходимости вызывать to_s
для этого.]
Я бы рекомендовал прочитать статью Википедии о хэш-таблицах - думаю, она поможет вам понять, что на самом деле означает {:ruby => "red"}
.
Еще одно упражнение, которое может помочь вам понять ситуацию: рассмотрим {1 => "red"}
. Семантически это не означает "установить значение 1
в "red"
", что невозможно в Ruby. Скорее, это означает "создать объект Hash и сохранить значение "red"
для ключа 1
.