Чому творець Рубі вирішив використовувати поняття Символи?


15

tl; dr: Чи існувало б мовно-агностичне визначення символів та причина їх використання іншими мовами?

Отже, чому творець Рубі використав поняття symbolsв мові?

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

Основне питання полягає в тому, чи існує концепція symbolsв Ruby для продуктивності чи це щось необхідне через те, як мова написана?

Чи була б програма в Ruby легшою та / або швидшою, ніж її, скажімо, аналог Python або Javascript? Якщо так, то це було б через це symbols?

Оскільки одним із намірів Рубі є легко читати та писати людині, чи не могли б її творці полегшити процес кодування, впровадивши ці вдосконалення в самому інтерпретаторі (як це може бути в інших мовах)?

Схоже, всі хочуть знати лише, що таке symbols, і як ними користуватися, а не чому вони там в першу чергу.


У Scala є символи, у верхній частині моєї голови. Я думаю, що багато Ліпс робить.
D. Ben Knoble

Відповіді:


17

Творець Рубі, Юкіхіро "Матц" Мацумото, розмістив пояснення про те, як на Рубі впливали Лісп, Малталк, Перл (а Вікіпедія говорить, що Ада і Ейфель теж):

Ruby - це мова, розроблена в наступних кроках:

  • скористайтеся простою мовою lisp (наприклад, до CL).
  • видалити макроси, s-вираз.
  • додати просту об’єктну систему (набагато простіше, ніж CLOS).
  • додайте блоки, натхненні функціями вищого порядку.
  • додати методи, знайдені в Smalltalk.
  • додайте функціональні можливості, знайдені в Perl (способом OO).

Отже, Рубі спочатку теоретично був Ліспом.

Давайте назвемо це MatzLisp відтепер. ;-)

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

У Ліспі такі символи - це першокласні ресурси, розміщені в різних пакетах, а це означає, що ви можете додавати свіжі символи під час виконання, прив’язувати їх до різного роду об’єктів. Це корисно при метапрограмуванні, оскільки ви можете бути впевнені, що у вас не буде зіткнення імен з іншими частинами коду.

Також символи інтерновані в час читання і можуть порівнюватися за ідентичністю, що є ефективним способом отримання нового виду значень (як числа, але абстрактні). Це допоможе написати код, коли ви використовуєте символьні значення безпосередньо, замість того, щоб визначати власні типи перерахунків, підкріплені цілими числами. Також кожен символ може містити додаткові дані. Так, наприклад, Emacs / Slime може приєднувати метадані з Emacs прямо до списку властивостей символу.

Поняття символу є центральним у Ліспі. Подивіться, наприклад, на PAIP (парадигми програмування штучного інтелекту: приклади досліджень у Common Lisp, Norvig) для детальних прикладів.


5
Гарна відповідь. Однак я не погоджуюся з Матцом: я б ніколи не думав називати мову без макросів нарічним діалектом. Функції метапрограмування програми Lisp - це саме та річ, яка надає цій мові приголомшливу силу, доповнюючи її безпроблемно-спрощену, невиразну граматику.
cmaster - відновлення моніки

11

Отже, чому творцям Рубі довелося використовувати поняття symbolsв мові?

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

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

Ви не сказали, що таке "багато інших мов", але ось лише невеликий уривок мов, що мають Symbolтип даних, як Рубі:

Існують також інші мови, які надають функції Symbols в іншій формі. Наприклад, у Java особливості Ruby's Stringрозділені на два (фактично три) типи: Stringі StringBuilder/ StringBuffer. З іншого боку, особливості типу Ruby Symbolскладені у Stringтип Java: Java Strings може бути інтернований , буквальні рядки та Strings, які є результатом константних виразів часу компіляції, автоматично інтернуються, динамічно генеровані Strings можуть бути інтерновані шляхом виклику String.internметод. Інтернований Stringв Java точно такий же, як Symbolу Ruby, але він не реалізований як окремий тип, це просто інший стан, ніж JavaString(Примітка. У більш ранніх версіях Ruby, які String#to_symраніше називались, String#internі цей метод існує і сьогодні як попередній псевдонім.)

Основним питанням може бути: чи існує концепція symbolsв Ruby як наміри виконання над собою та іншими мовами,

Symbols - це перш за все тип даних із конкретними семантикою . Ці семантики також дозволяють реалізувати деякі оперативні дії (наприклад, швидке тестування рівності O (1)), але це не головна мета.

чи просто щось, що потрібно для існування через спосіб написання мови?

Symbols взагалі не потрібні мові Ruby, без них Ruby буде добре працювати. Вони суто бібліотечна особливість. Є точно одне місце в мові, яке пов'язане з Symbols: defвираз визначення методу оцінюється до Symbolпозначення імені методу, який визначається. Однак це досить недавня зміна, до цього повернене значення просто не було визначено. МРТ просто оцінювали nil, Рубіній оцінював Rubinius::CompiledMethodоб'єкт тощо. Це також можна було б оцінити доUnboundMethod … чи просто а String.

Чи буде програма в Ruby легшою та / або швидшою, ніж її, скажімо, аналог Python або Node? Якщо так, то це було б черезsymbols ?

Я не впевнений, що ви тут просите. Продуктивність - це переважно питання якості впровадження, а не мови. Крім того, Node навіть не є мовою, це рівномірний фреймворк вводу / виводу для ECMAScript. Запуск еквівалентного сценарію на IronPython та MRI, IronPython, швидше за все, буде швидшим. Запуск еквівалентного сценарію на CPython та JRuby + Truffle, JRuby + Truffle, швидше за все, буде швидшим. Це не має нічого спільногоSymbol s, але з якістю реалізації: JRuby + Truffle має агресивно оптимізуючий компілятор, а також весь механізм оптимізації високопродуктивного JVM, CPython - це простий інтерпретатор.

Оскільки одним із намірів Рубі є легко читати та писати людині, чи не могли її творці полегшити процес кодування, впровадивши ці вдосконалення в самому інтерпретаторі (як це може бути в інших мовах)?

Symbols не є оптимізацією компілятора. Вони є окремим типом даних із специфічною семантикою. Вони не схожі на флори YARV , які є приватною внутрішньою оптимізацією для Floats. Ситуація не така, як Integer, Bignumі Fixnum, що має бути непомітною деталлю внутрішньої оптимізації, але, на жаль, не є. (Це, нарешті, буде виправлено у Ruby 2.4, який видаляєтьсяFixnum і Bignumі листя просто Integer.)

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

Чи існувало б мовно-агностичне визначення символів та причина їх застосування іншими мовами?

Symbol- це тип даних, що позначає поняття імені або етикетки . Symbols - ціннісні об'єкти , незмінні, як правило, негайні (якщо мова відрізняє таку річ), без громадянства і не мають ідентичності. Два Symbolрівні, які рівні, також гарантуються однаковими, іншими словами, двомаSymbol s, які рівні, насправді однакові Symbol. Це означає, що ціннісна рівність і референтна рівність - це одне і те ж, і, отже, рівність є ефективною і O (1).

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

У родині Лісп, наприклад, немає поняття "змінна". Натомість у вас єSymbol пов'язані значення.

У мовах з відбивають або самосозерцательнимі можливостями, Symbols часто використовується для позначення назви відображених сутностей в API , відображення, наприклад , в Ruby, Object#methods, Object#singleton_methods, Object#public_methods, Object#protected_methods, і Object#public_methodsповертати Arrayз Symbolї (хоча вони могли б точно так же повертати Arrayз Methodс). Object#public_sendприймає Symbolпозначення імені повідомлення для надсилання в якості аргументу (хоча воно також приймає aString а, Symbolє більш семантично правильним).

У ECMAScript Symbols є основоположним складовим елементом забезпечення безпечності можливостей ECMAScript у майбутньому. Вони також відіграють велику роль у роздумах.


Атоми Ерланга були взяті безпосередньо з Пролога (Роберт Вірдінг сказав мені, що в якийсь момент)
Захарій К

2

Символи корисні в Ruby, і ви побачите їх у всьому коді Ruby, оскільки кожен символ повторно використовується щоразу, коли на нього посилається. Це поліпшення продуктивності для рядків, оскільки кожне використання рядка, яке не збережено у змінній, створює новий об’єкт у пам'яті. Наприклад, якщо я використовую один і той же рядок кілька разів як хеш-ключ:

my_hash = {"a" => 1, "b" => 2, "c" => 3}
100_000.times { |i| puts my_hash["a"] }

Рядок "a" створюється в пам'яті 101 000 разів. Якщо я замість цього використовував символ:

my_hash = {a: 1, b: 2, c: 3}
100_000.times { |i| puts my_hash[:a] }

Символ :a все ще є одним об’єктом у пам'яті. Це робить символи набагато ефективнішими, ніж рядки.

ОНОВЛЕННЯ Ось базовий показник (взятий з Codecademy ), який демонструє різницю у продуктивності:

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
  100_000.times { string_AZ["r"] }
end

symbol_time = Benchmark.realtime do
  100_000.times { symbol_AZ[:r] }
end

puts "String time: #{string_time} seconds."
puts "Symbol time: #{symbol_time} seconds."

Ось мої результати для мого MBP:

String time: 0.1254125550040044 seconds.
Symbol time: 0.07360960397636518 seconds.

Існує чітка різниця у використанні рядків та символів для просто ідентифікації ключів у хеші.


Я не впевнений, чи це так. Я очікував, що реалізація Ruby виконає один і той же код кілька разів, не аналізуючи код знову і знову для кожної ітерації. Навіть якщо кожне лексичне явище "a"справді є новим рядком, я думаю, що у вашому прикладі буде рівно два "a"(і реалізація може навіть поділити пам'ять, поки одна з них не буде вимкнена). Для того щоб створити мільйони рядків, вам, ймовірно, потрібно буде використовувати String.new ("a"). Але я не добре розбираюся в Рубі, тому, можливо, я помиляюся.
coredump

1
На одному з уроків Codecademy вони генерують орієнтир для рядків проти символів, як у моєму прикладі. Я додам це до відповіді.
Кіт Маттікс

1
Дякуємо за додавання еталону. Ваш тест показує очікуване посилення, отримане за допомогою символів замість рядків, завдяки більш швидкому тестуванню в хешбл (ідентифікація порівняно з рядком), але немає ніякого способу зробити висновок про те, що рядки виділяються при кожній ітерації. Я додав версію string_AZ[String.new("r")], щоб побачити, чи це має значення. Я отримую 21 мс для рядків (оригінальна версія), 7 мс із символами та 50мм зі свіжими рядками щоразу. Тож я б сказав, що рядки не виділяються стільки в буквальній "r"версії.
coredump

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