Как понимать символы в Ruby - PullRequest
       21

Как понимать символы в Ruby

81 голосов
/ 26 февраля 2010

Несмотря на чтение " Понимание символов Ruby ", я все еще смущен представлением данных в памяти, когда речь заходит об использовании символов. Если символ, два из которых содержатся в разных объектах, существует в одной и той же ячейке памяти, то как получается, что они содержат разные значения? Я ожидал, что одна и та же ячейка памяти будет содержать одно и то же значение.

Это цитата по ссылке:

В отличие от строк, символы с одинаковыми именами инициализируются и существуют в памяти только один раз во время сеанса 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"?

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

  • Можно ли присвоить значение символу?
  • Является ли символ просто указателем на переменную со значением в ней?
  • Если символы глобальные, значит ли это, что символ всегда указывает на одну вещь?

Ответы [ 11 ]

60 голосов
/ 26 февраля 2010

Учтите это:

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"}

Это создает только пять объектов в памяти - один символ, две строки и два хеш-объекта.

49 голосов
/ 26 февраля 2010

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

Просто думайте о символах как о постоянных строках.

32 голосов
/ 26 февраля 2010

Символ :ruby не содержит "red" или "programming". Символ :ruby является просто символом :ruby. Это ваши хэши, patient1 и patient2, каждый из которых содержит эти значения, в каждом случае указываемые одним и тем же ключом.

Подумайте об этом так: если вы войдете в гостиную в рождественское утро и увидите две коробки с бирками, на которых написано «Кеззер». В нем есть носки, а в другом - уголь. Вы не запутаетесь и не спросите, как «Кеззер» может содержать как носки, так и уголь, даже если это одно и то же имя. Потому что имя не содержит (дрянные) подарки. Это просто указывает на них. Точно так же :ruby не содержит значений в вашем хэше, он просто указывает на них.

26 голосов
/ 26 февраля 2010

Возможно, вы предполагаете, что объявление, которое вы сделали, определяет значение Symbol как нечто иное, чем оно есть. Фактически, Symbol - это просто «внутреннее» значение String, которое остается постоянным. Именно потому, что они хранятся с использованием простого целочисленного идентификатора, они часто используются, поскольку это более эффективно, чем управление большим количеством строк переменной длины.

Возьмите пример вашего примера:

patient1 = { :ruby => "red" }

Это должно читаться как: «объявить переменную Patient1 и определить ее как Hash, а в этом хранилище значение« red »под ключом (символ« ruby ​​»)»

Другой способ написать это:

patient1 = Hash.new
patient1[:ruby] = 'red'

puts patient1[:ruby]
# 'red'

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

Концепция Symbol может быть немного запутанной, поскольку она не характерна для большинства других языков.

Каждый объект 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

Аналогично, преобразование из Symbol в String каждый раз создает отдельную строку:

[ :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, эта таблица будет расти.

15 голосов
/ 26 февраля 2010

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

12 голосов
/ 26 февраля 2010
patient1.each_key {|key| puts key.to_s}

Тогда что будет выводиться? "красный" или "Программирование"?

Ни один из них не выдаст "ruby".

Вы путаете символы и хэши. Они не связаны, но они полезны вместе. Соответствующий символ :ruby; он не имеет ничего общего со значениями в хэше, и его внутреннее целочисленное представление всегда будет одинаковым, а его «значение» (при преобразовании в строку) всегда будет «ruby».

10 голосов
/ 04 сентября 2013

Короче

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

Почему: красный лучше, чем "красный"

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

Однако было бы эффективнее использовать числовой индекс вместо строковых ключей. Таким образом, символы были введены как компромисс между скоростью и удобочитаемостью. Символы разрешаются намного проще, чем эквивалентные строки. Будучи удобочитаемым для человека и легким для времени выполнения для распознавания символов, является идеальным дополнением к динамическому языку.

Преимущества

Поскольку символы являются неизменяемыми, они могут использоваться совместно во время выполнения. Если два экземпляра хэша имеют общую лексикографическую или семантическую потребность в красном элементе, то символ: красный будет использовать примерно половину памяти, которая требуется строке «красный» для двух хешей.

Поскольку: red всегда разрешается обратно в одно и то же место в памяти, его можно повторно использовать в сотнях экземпляров хеша почти без увеличения объема памяти, тогда как использование «red» добавит стоимость памяти, поскольку каждый экземпляр хеша должен будет хранить изменяемая строка при создании.

Не уверен, как Ruby на самом деле реализует символы / строки, но ясно, что символ предлагает меньше накладных расходов на реализацию во время выполнения, поскольку это фиксированное представление. Символы «плюс» набирают на один символ меньше, чем строка в кавычках, и меньше набирает - вечное стремление настоящих рубиистов.

Основная информация

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

3 голосов
/ 26 февраля 2010

Я бы рекомендовал прочитать статью Википедии о хеш-таблицах - думаю, это поможет вам понять, что на самом деле означает {:ruby => "red"}.

Еще одно упражнение, которое может помочь вам понять ситуацию: рассмотрим {1 => "red"}. Семантически это не означает «установить значение 1 в "red"», что невозможно в Ruby. Скорее это означает «создать объект Hash и сохранить значение "red" для ключа 1.

3 голосов
/ 26 февраля 2010
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}

Тогда что будет выводиться? "красный" или "программирование"?

Ни один, конечно. Выход будет ruby. Который, кстати, вы могли бы выяснить за меньшее время, чем потребовалось, чтобы набрать вопрос, просто вместо этого набрав его в IRB.

Почему будет это будет red или programming? Символы всегда оценивают сами. Значением символа :ruby является сам символ :ruby, а строковым представлением символа :ruby является значение строки "ruby".

[Кстати: puts всегда преобразует свои аргументы в строки, в любом случае. Там нет необходимости звонить to_s на это.]

0 голосов
/ 10 января 2015

Я новичок в Ruby, но я думаю (надеюсь?), Что это простой способ взглянуть на это ...

Символ не является переменной или константой. Оно не соответствует или не указывает на значение. Символ - это значение.

Все, что есть, это строка без заголовка объекта. Текст и только текст.

Итак, это:

"hellobuddy"

То же самое, что и это:

:hellobuddy

За исключением того, что вы не можете, например, сделать: hellobuddy.upcase. Это строковое значение и ТОЛЬКО строковое значение.

Аналогично, это:

greeting =>"hellobuddy"

То же самое, что и это:

greeting => :hellobuddy

Но, опять же, без заголовка строкового объекта.

...