Як зрозуміти символи в Ruby


85

Незважаючи на прочитання " Розуміння символів 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"?

Забувши хеші на секунду, я думаю, що символ є вказівником на значення. У мене є такі запитання:

  • Чи можу я призначити значення символу?
  • Чи є символ просто вказівником на змінну зі значенням у ній?
  • Якщо символи глобальні, чи означає це, що символ завжди вказує на одне?

1
Він виведе ": ruby", оскільки ви друкуєте символ. Якщо ви скажете puts patient1[:ruby], він надрукує "червоний", якщо ви скажете puts patient2[:ruby], він надрукує "програмування".
ROSS

1
Символ - НЕ вказівник на значення. Внутрішньо символом є лише ціле число.
akuhn

Відповіді:


62

Розглянемо це:

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

Це створює лише п'ять об'єктів у пам'яті - один символ, два рядки та два хеш-об'єкти.


Приклад у посиланні, однак, показує символи, що містять різні значення, але символ має однакову назву та однакове місце в пам'яті. Коли вони виводяться, вони мають різні значення, це частина, яку я не отримую. Напевно вони повинні містити одне і те ж значення?
Kezzer

1
Я щойно зробив редагування, щоб спробувати пояснити, як я все ще розгублений. Мій мозок не може обчислити;)
Kezzer

48
Символи не містять значень, вони є значеннями. Хеші містять значення.
Младен Ябланович

5
Саме Hash(створена {... => ...} у вашому коді) зберігає пари ключ / значення, а не Symbolсамі s. В Symbolи (наприклад , :symbolчи , :symабо :ruby) є ключами в парах. Тільки як частина Hashвиконання вони "вказують" на що-небудь.
James A. Rosen

1
Символ використовується як ключ у хеші, а не як значення, тому вони можуть бути різними, це подібно до використання приказки key1 = 'ruby' та hash1 = {key1 => 'value' ...} hash2 = { key1 => 'value2' ...}.
Джошуа Олсон,

53

Я міг перебирати символи, коли думав про це так. Рядок Ruby - це об’єкт, що має купу методів та властивостей. Люди люблять використовувати рядки для ключів, і коли рядок використовується для ключа, тоді всі ці додаткові методи не використовуються. Тож вони зробили символи, які є рядковими об’єктами з усіма видаленими функціональними можливостями, за винятком того, який потрібен для того, щоб він був хорошим ключем.

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


2
Читаючи дописи, цей, мабуть, для мене найбільш доцільний. : ruby ​​просто зберігається десь у пам'яті, якщо я десь використовую "ruby", то знову десь знову "ruby", це просто дублювання. Отже, використання символів - це спосіб зменшити дублювання загальних даних. Як ви кажете, постійні рядки. Я думаю, існує якийсь основний механізм, який знову знайде цей символ для використання?
Kezzer

@Kezzer Ця відповідь дійсно хороша і мені здається правильною, але ваш коментар говорить про щось інше і є помилковим або оманливим, ваш коментар говорить про дублювання даних за допомогою рядків, і що це причина символів, це неправильно або вводить в оману. символ кілька разів не буде використовувати більше місця в пам'яті, але ви можете мати це для рядків теж у багатьох мовах, наприклад, деякі мови програмування, якщо ви пишете "abc", а в іншому місці "abc" компілятор бачить, що це той самий рядок значення, і зберігає його в тому самому місці, що робить його тим самим об'єктом, що називається рядовим інтернуванням, а c # робить це.
Барлоп

Отже, це в основному неймовірно легка версія струни?
stevec

34

Символ :rubyне містить "red"або "programming". Символ :ruby- це просто символ :ruby. Це ваші хеші, patient1і patient2кожен з них містить ці значення, в кожному випадку вказані одним і тим же ключем.

Подумайте про це так: якщо ви зайдете у вітальню на Різдвяний ранок і побачите на них дві коробки з позначкою, на яких написано "Kezzer". На ній шкарпетки, а на другій - вугілля. Ви не збираєтесь заплутатися і запитати, як "Kezzer" може містити як шкарпетки, так і вугілля, хоча це одна і та ж назва. Оскільки назва не містить (безглуздих) подарунків. Це просто вказує на них. Аналогічним чином, :rubyне містить значень у вашому хеші, він просто вказує на них.


2
Ця відповідь має повний сенс.
Vass

Це звучить як повне змішування хешів та символів. Символ не вказує на значення, якщо ви хочете сказати, що це відбувається, коли є хеш, ну, це може бути спірним, але символ не повинен бути в хеші. Можна сказати, mystring = :steveT символ не вказує ні на що. Ключ у хеші має пов'язане значення, і ключ може бути символом. Але символ не повинен бути в хеші.
Барлоп

27

Ви можете припустити, що декларація, яку ви зробили, визначає значення символу як щось інше, ніж те, що воно є. Насправді Символ - це просто "внутрішнє" значення рядка, яке залишається незмінним. Завдяки тому, що вони зберігаються за допомогою простого цілочисельного ідентифікатора, їх часто використовують, оскільки це ефективніше, ніж керування великою кількістю рядків змінної довжини.

Візьмемо приклад з вашого прикладу:

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

Аналогічним чином, перетворення з 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, ця таблиця буде зростати.


17

Символи не є покажчиками. Вони не містять значень. Символи просто є . :rubyє символом, :rubyі це все, що від нього є. Він не містить значення, він нічого не робить , він просто існує як символ :ruby. Символ :ruby- це значення, як і число 1. Це не вказує на інше значення не більше, ніж число 1.


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

Тоді що буде виведено? "червоний", або "програмування"?

Ні, він виведе "рубін".

Ви плутаєте символи та хеші. Вони не пов’язані між собою, але корисні разом. Символом, про який йде мова, є :ruby; це не має нічого спільного зі значеннями в хеші, і це внутрішнє цілочисельне подання завжди буде однаковим, а його "значення" (при перетворенні в рядок) завжди буде "ruby".


10

Коротко

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

Чому: червоний краще, ніж "червоний"

У динамічних об’єктно-орієнтованих мовах ви створюєте складні, вкладені структури даних із читабельними посиланнями. Хеш є загальним Прецедент , де карта значень унікальних ключів - унікальних, по крайней мере, в кожному конкретному випадку. Ви не можете мати більше одного "червоного" ключа на хеш.

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

Переваги

Оскільки символи незмінні, їх можна спільно використовувати протягом усього часу виконання. Якщо два екземпляри хешу мають спільну лексикографічну або семантичну потребу в елементі червоного кольору, то символ: червоний використовує приблизно половину пам'яті, яку рядок "червоний" потребував би для двох хешів.

Оскільки: червоний завжди вирішує те саме місце в пам'яті, його можна використовувати повторно в сотні екземплярів хешу майже без збільшення пам'яті, тоді як використання "червоного" додасть вартості пам'яті, оскільки кожен екземпляр хешу повинен зберігати змінний рядок на створення.

Не впевнений, як Ruby насправді реалізує символи / рядок, але очевидно, що символ пропонує менші накладні витрати на реалізацію під час виконання, оскільки це фіксоване представлення. Плюс-символи набирають на один символ менше, ніж цитований рядок, і менше набору тексту - це вічне прагнення справжніх рубістів.

Резюме

З таким символом, як: червоний, ви отримуєте читабельність подання рядка з меншими накладними витратами через вартість операцій порівняння рядків та необхідність зберігати кожен екземпляр рядка в пам'яті.


4

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

Ще одна вправа, яка може допомогти вам зрозуміти ситуацію: розгляньте {1 => "red"}. Семантичний, це не означає , що «встановити значення 1для "red"", що неможливо в Ruby. Швидше, це означає "створити хеш- об'єкт і зберегти значення "red"ключа 1.


3
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.]


У мене немає IRB на поточній машині, і я б не міг встановити його, отже, чому, тому вибачаюся за це.
Kezzer

2
@Kezzer: Не хвилюйся, мені було просто цікаво. Іноді ти настільки глибоко зариваєшся в проблему, що навіть не бачиш найпростіших речей. Коли я в основному вирізав і вставив ваше питання в IRB, я просто здивувався: "чому він цього не зробив сам?" І не хвилюйтеся, ви не перший (і не останній), хто запитує "що робить цей друк", коли відповідь "просто запустіть!" ДО: ось ваш миттєвий IRB, де завгодно, будь-коли та в будь-який час, установка не потрібна: TryRuby.Org або Ruby-Versions.Net надає SSH доступ до всіх версій МРТ, коли-небудь випущених + YARV + JRuby + Rubinius + REE.
Jörg W Mittag

Дякую, просто пограти з цим зараз. Я все ще трохи розгублений, хоча так переглядаю це ще раз.
Kezzer

0

Я новачок у Рубі, але я думаю (сподіваюся?) Це простий спосіб подивитися на це ...

Символ не є змінною чи константою. Це не означає або не вказує на значення. Символ - це значення.

Все це, є рядок без накладних витрат на об’єкт. Текст і лише текст.

Отже, це:

"hellobuddy"

Це те саме, що це:

:hellobuddy

Окрім того, що ви не можете зробити, наприклад,: hellobuddy.upcase. Це значення рядка та ТІЛЬКИ значення рядка.

Так само це:

greeting =>"hellobuddy"

Це те саме, що це:

greeting => :hellobuddy

Але, знову ж таки, без накладних витрат на об’єкт рядка.


-1

Один з простих способів обернути голову навколо цього - подумати: "а якби я використовував рядок, а не символ?

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", залежно від того, як ви визначили хеш.

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


в чому помилка або помилка в цьому поясненні, голоси проти? цікаво заради навчання.
ahnbizcad

те, що аналогія може бути для когось неприємною, ще не означає, що вона є хибною.
ahnbizcad

2
Я не бачу жодних помилок, обов'язково, але надзвичайно складно визначити, що ви намагаєтесь сказати у цій відповіді.
Реверсивна інженерія

x взаємодіє та концептуалізується по-різному залежно від контексту / сутності, що здійснює інтерпретацію. досить просто.
ahnbizcad

переглянув відповідь. Дякуємо за відгук.
ahnbizcad
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.