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

Несмотря на чтение "Понимание символов 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"?

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

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

person Kezzer    schedule 26.02.2010    source источник
comment
Будет выведено: ruby, потому что вы печатаете символ. Если вы скажете puts patient1[:ruby], он напечатает красный цвет, если вы скажете puts patient2[:ruby], он напечатает программирование.   -  person R.O.S.S    schedule 19.07.2016
comment
Символ НЕ является указателем на значение. Внутренне символ - это просто целое число.   -  person akuhn    schedule 24.12.2016


Ответы (11)


Учти это:

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

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

person anshul    schedule 26.02.2010
comment
Однако пример в ссылке показывает символы, содержащие разные значения, но символ имеет то же имя и тот же участок памяти. Когда они выводятся, у них есть разные значения, этого я не понимаю. Неужто они должны содержать одно и то же значение? - person Kezzer; 26.02.2010
comment
Я только что отредактировал, чтобы попытаться объяснить, в чем я до сих пор запутался. Мой мозг не умеет вычислять;) - person Kezzer; 26.02.2010
comment
Символы не содержат значений, они являются значениями. Хеши содержат значения. - person Mladen Jablanović; 26.02.2010
comment
Именно Hash (созданный {... = ›...} в вашем коде) хранит пары ключ / значение, а не сами Symbols. Symbol (например, :symbol, :sym или :ruby) - это ключи в парах. Только как часть Hash они указывают на что-нибудь. - person James A. Rosen; 26.02.2010
comment
Символ используется как ключ в хеш-коде, а не как значение, поэтому они могут быть разными, это похоже на использование слов key1 = 'ruby' и hash1 = {key1 = ›'value' ...} hash2 = { ключ1 = ›'значение2' ...}. - person Joshua Olson; 07.10.2013

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

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

person Segfault    schedule 26.02.2010
comment
Читая посты, я думаю, что этот, наверное, имеет наибольший смысл. : ruby ​​просто хранится где-то в памяти, если я где-то использую ruby, затем снова где-то ruby, это просто дублирование. Таким образом, использование символов - это способ уменьшить дублирование общих данных. Как вы говорите, постоянные строки. Думаю, есть какой-то базовый механизм, который снова найдет этот символ для использования? - person Kezzer; 05.03.2010
comment
@Kezzer Этот ответ действительно хорош и кажется мне правильным, но ваш комментарий говорит о другом и неверен или вводит в заблуждение, ваш комментарий говорит о дублировании данных со строками и о том, что это причина символов, что неверно или вводит в заблуждение.Да, используя символ несколько раз не будет занимать больше места в памяти, но вы можете использовать это и для строк на многих языках, например в некоторых языках программирования, если вы пишете abc, а где-то еще abc, компилятор видит одну и ту же строку значения и сохраняет ее в том же месте, что делает его одним и тем же объектом, что называется интернированием строк, и C # делает это. - person barlop; 31.05.2018
comment
Значит, это невероятно легкая версия струны? - person stevec; 20.09.2020

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

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

person jcdyer    schedule 26.02.2010
comment
Этот ответ имеет смысл. - person Vass; 29.10.2014
comment
Это звучит как полная смесь хэшей и символов. Символ не указывает на значение, если вы хотите сказать, что он указывает в хеше, что ж, это может быть спорным, но символ не обязательно должен быть в хеше. Вы можете сказать mystring = :steveT, что символ ни на что не указывает. Ключ в хэше имеет связанное значение, и ключ может быть символом. Но символ не обязательно должен быть хешем. - person barlop; 31.05.2018

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

Рассмотрим ваш пример:

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

person tadman    schedule 26.02.2010

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

person Chuck    schedule 26.02.2010

patient1.each_key {|key| puts key.to_s}

Тогда что будет на выходе? «красный» или «программирование»?

И "рубин" тоже не будет.

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

person meagar    schedule 26.02.2010

Короче

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

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

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

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

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

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

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

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

Резюме

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

person Mark Fox    schedule 03.09.2013

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

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

person Greg Campbell    schedule 26.02.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".

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

person Jörg W Mittag    schedule 26.02.2010
comment
У меня нет IRB на текущей машине, и поэтому я не смогу установить его, поэтому приношу свои извинения за это. - person Kezzer; 26.02.2010
comment
@Kezzer: Не беспокойся, мне было просто любопытно. Иногда вы так глубоко погружаетесь в проблему, что больше не можете видеть даже самых простых вещей. Когда я в основном вырезал и вставил ваш вопрос в IRB, я просто подумал: почему он сам этого не сделал? И не волнуйтесь, вы не первый (и не последний), кто спрашивает, что это за печать, когда ответ - просто запустить! Кстати: вот ваш мгновенный IRB в любом месте и в любое время, установка не требуется: TryRuby.Org или Ruby-Versions.Net дает вам SSH-доступ ко всем когда-либо выпущенным версиям MRI + YARV + JRuby + Rubinius + REE. - person Jörg W Mittag; 26.02.2010
comment
Спасибо, просто поиграю с этим сейчас. Я все еще немного сбит с толку, поэтому повторюсь еще раз. - person Kezzer; 26.02.2010

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

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

Все, что это есть, - это строка без накладных расходов на объект. Текст и только текст.

Итак, это:

"hellobuddy"

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

:hellobuddy

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

Точно так же это:

greeting =>"hellobuddy"

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

greeting => :hellobuddy

Но, опять же, без накладных расходов на строковый объект.

person Dave Munger    schedule 10.01.2015

Один из простых способов понять это - подумать: «А что, если бы я использовал строку, а не символ?

patient1 = { "ruby" => "red" }
patient2 = { "ruby" => "programming" }

Это совсем не сбивает с толку, правда? Вы используете рубин в качестве ключа в хэше.

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

Затем хеш-код выглядит следующим образом: «Какое у меня значение ключа? О, это "ruby". Затем это значение сопоставляется с« красным »или« программированием ». Другими словами, :ruby не разыменовывается на "red" или "programming". Хэш отображает От :ruby до "red" или "programming".

Сравните это с использованием символов

patient1 = { :ruby => "red" }
patient2 = { :ruby => "programming" }

Значение :ruby также равно "ruby", по сути.

Почему? Потому что символы по сути являются строковыми константами. У констант нет нескольких экземпляров. Это тот же адрес памяти. И адрес памяти имеет определенное значение после разыменования. Для символов имя указателя - это символ, а разыменованное значение - это строка, которая соответствует имени символа, в данном случае "ruby".

Находясь в хэше, вы используете не символ, указатель, а определяемое значение. Вы используете не :ruby, а "ruby". Затем хеш ищет ключ "ruby", значение равно "red" или "programming", в зависимости от того, как вы определили хеш.

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

person ahnbizcad    schedule 10.05.2014
comment
В чем заключается заблуждение или ошибка в этом объяснении, противники? любопытно ради познания. - person ahnbizcad; 18.01.2017
comment
Просто потому, что аналогия может показаться кому-то неприятной, не означает, что она ошибочна. - person ahnbizcad; 19.01.2017
comment
Я не вижу ошибок, обязательно, но очень сложно уловить то, что вы пытаетесь сказать в этом ответе. - person Reverse Engineered; 07.03.2018
comment
x интерпретируется и концептуализируется по-разному в зависимости от контекста / объекта, выполняющего интерпретацию. довольно просто. - person ahnbizcad; 07.03.2018
comment
переработал ответ. Спасибо за ответ. - person ahnbizcad; 02.03.2019