Чому ідентифікатори не повинні починатися з числа?


32

Здається, більшість мов програмування розроблені так, щоб не дозволяти оголошувати ідентифікатор, який починається з числа. Мені просто цікаво було знати причину. Я вже шукав в Інтернеті, але не зміг знайти задовільне пояснення.


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

5
@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...
невідомо користувачеві

6
Далі це дозволяє. Із вбудованих: 2DUP, 2DROP, 2SWAP, 2> R, 2R @, 2R>, 0 = тощо.
Пітер Мортенсен

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

Відповіді:


51

У C / C ++ число, за яким йде буква, вважається числовою константою, а рядок, що випливає, кваліфікує тип константи. Так, наприклад (це VC ++, не впевнені, наскільки вони стандартні):

  • 0 - підписане ціле число
  • 0л - підписане довге ціле число
  • 0u - непідписане ціле число
  • 0i64 - ціле число, підписане 64 бітами

Отже, а) лексеру легше, як сказав Даніель, але також б) він чітко розрізняє, оскільки 0y може бути змінною, але 0u ніколи не буде. Плюс інші класифікатори, такі як "i64", додаються набагато пізніше, ніж "l" або "u", і вони хочуть залишати опцію відкритою, додаючи більше, якщо потрібно.


7
також шістнадцяткові числа записуються у вигляді 0xd +, де d + - це ще 1 шістнадцяткова цифра 0-f - значить, 0xbeef - цілком дійсне "число".
tcrosley

20
ви, хлопці, розумієте, що я не збирався на мовну специфікацію, але навели лише кілька прикладів, щоб проілюструвати суть, правда?
DXM

6
Re: "вони хочуть залишати опцію відкритим додавати більше, якщо потрібно": а C ++ 11 навіть дозволяє додати свій власний; див. http://en.wikipedia.org/wiki/C++11#User-defined_literals .
ruakh

2
Я не думаю, що це правильне пояснення. Правило "ідентифікатор не може починатися з цифри" було вірно для Algol, Pascal та інших мов, які не дозволяють алфавітному суфіксам числових констант.
Ларрі Гріц

1
@LarryGritz: "Послідовне розділення слів пробілами стало загальним звичаєм приблизно в десятому столітті нашої ери і тривало приблизно до 1957 року, коли FORTRAN відмовився від практики". —Запустити довідник FORTRAN (з вікі). У Фортрана були свої особливі причини, оскільки вони вирішили, що місця взагалі не є обов'язковими. СУЧАСНІ мови, як і їх пробіл. Ви на самоті з Алголом, але я не такий вже й сучасний. З іншого боку, всі C / C ++ / C # / F # мають суфікси.
DXM

49

Зручність людей, що реалізують лексеру. (Ні, серйозно, це стосується цього. У різних мов є й інші причини, але це зрештою зводиться до цього.)


2
Легко було б розрізнити інтегральні літерали та ідентифікатори, починаючи з цифр, використовуючи PEG або інші сучасні методи розбору. Навіть компілятори, що використовують примітивні лексеми, можуть поставити їх у одну категорію лексем і розмежувати пізніше. Було б просто незручно, якби наприклад 0fluбуло буквальним і 0gluбуло локальним ідентифікатором.
Даніель Любаров

2
Люди абсолютно можуть їх розрізнити. Рішення приймається виходячи з зручності (або, якщо ви менш благодійні, лінь), а не технічних вимог.
Даніель Пітман

2
@DanielPittman: Вам знадобиться семантичний аналіз, щоб зробити будь-яку надійну розбірливість, щоб цього не можна було зробити в лексемі. Виштовхування рішення з лексеру робить парсер складнішим, і в чому користь? Окрім дуже поганої ситуації з витратами та вигодами, просто немає хорошого способу вирішити такий вигляд, як, int 0u = 5; unsigned int x = 0u;однак, ви вирішите визначити тлумачення цього коду (ймовірно, x == 0 або x == 5), люди будуть плутати через неоднозначність. Навіть якби банально реалізувати компілятор таким чином, хороший дизайнер цього, швидше за все, не зробив би.
Жорен

10
Головна зручність - це для розбору в моїй голові, а не для творця мови.
CodesInChaos

2
Для багатьох людей все ще несподівано дізнатися, що лексичний аналіз, як правило, є головним фактором, найбільш повільним етапом укладача / перекладача.
hippietrail

20

Розглянемо наступні 2 випадки:

Випадок 1

Припустимо, що ідентифікатор може починатися з числа.

Отже, твердження, як показано нижче, буде дійсним (оскільки ідентифікатор може мати 1 або більше символів):

int 3;

Коли я намагаюся використовувати вищезазначену змінну в програмі, це призведе до неоднозначності компілятора:

int 3, a;
3 = 5;
a = 3;

У твердженні a=3яка роль 3 (це змінна зі значенням 5 чи це цифра 3)?

Випадок 2

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

  • Мовні правила щодо змінної, яка говорить про те, що змінна може складатися з 1 або більше символів, доведеться переглядати на складне правило типу: Змінна може мати один або більше символів і повинна бути унікальною, якщо вона не починається з числа вона не може бути однієї символьної довжини, починаючи з числа (тощо).

  • Компілятору доведеться перевіряти і повідомляти про випадки помилок, коли всі цифри (наприклад, 333) та дійсні суфікси алфавіту (наприклад, 34L) використовуються як імена змінних. У слабко типізованих мовах, таких як Python та JS, де ви можете використовувати змінні на льоту, не оголошуючи їх, можливо, навіть неможливо перевірити особливі випадки, що включають усі цифри, наприклад if (33==5), 33 тут може бути помилковою незадекларованою змінною, яку оголосив користувач. Але компілятор не зможе це визначити та повідомити про помилку.

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


2
За цією логікою ідентифікатори не могли містити символів, оскільки вони були б неоднозначними для ключових слів. Ви можете уявити, як катастрофічно int char = floatбуло б?
Паббі

4
@Pubby: Я не бачу, як ти можеш екстраполювати те, що я сказав, до якихось безглуздих сенсів, яких я поки не можу зрозуміти. Що означає ваш коментар?
aml90

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

@Pubby: Під неоднозначністю я мав на увазі, що компілятор не знатиме, в якому контексті я використовую ім'я змінної (навіть використовуючи лексичний пріоритет). Наприклад, розгляньте цей код: int 3,a; 3=5; a=3; У твердженні a = 3 інтерпретується 3 як ідентифікатор або як число? Це викликає неоднозначність. Сподіваюся, це зрозуміло.
aml90

2
Я також вважаю цей аргумент слабким. Було б тривіально записати лексер, який би приймав ідентифікатори, що починаються з, але не повністю складаються з цифр.
Ларрі Гріц

11

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

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

Було б цілком можливо спроектувати синтаксис мови, де числові літерали якимось чином цитувались, наприклад, tildas, тому числовий літерал для номера один був закодований як ~ 1 ~, і все, що не є ключовим словом і не вкладається в лапки, розглядалося як ім'я змінної .

Таким чином, ви можете кодувати заяви, як-от:

1 = ~2~
two = 1 * ~2~

Але також:

2 = ~3~
six = 2 + 2

Який би синтаксис ви не вибрали неоднозначно і важко дотримуватися код, неминуче.

Мова C та більшість мов "фігурних дужок", що походять від С, також вважають гарною ідеєю дозволити програмістам безпосередньо кодувати Octal і Hexadecimal літерали, а також вказати тип літералу, якщо це важливо. Так

010  // Octal 10 = 8;
0x10 // Hexadecimal 10 = 16;
5l   // long integer with decimal value 5
2.0d // double float with value 2

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

2lll = 22 // OK
2ll  = 2  // compiler error

Така двозначність не допомогла б нікому писати чи читати програму.

На прикладі тісно пов'язаного реального світу ви можете подивитися мову PL / 1, дизайнери якого вважали, що можливість використовувати ключові слова як імена змінних - це гарна ідея, щоб:

IF THEN THEN THEN = ELSE; ELSE ELSE = THEN;
IF IF THEN ELSE = IF; ELSE THEN = ELSE;
DO WHILE (WHILE = DO); END = WHILE + DO; END;

Дійсний код, який компілює та виконує.


C був розроблений як портативна збірка для Unix. Unix спочатку був розроблений для 18-бітної машини, де восьмигранник добре підходить для друку так само, як і шістнадцятковий. Отже, вони насправді потребували восьмерики.

Також для подвійного скручування (АБО, XOR, І, НЕ) та впровадження драйверів пристроїв важливо вказати точний розмір буквалу, а також значення!
Джеймс Андерсон

10

Фортран мав величезний вплив на те, як розроблялися пізніші мови. На початку (деякі з цих проблем були усунені) у Fortran майже не було правил, що обмежували б те, яке ім'я ви можете дати ідентифікатору. Це зробило мову надзвичайно важкою для розбору як для компіляторів, так і для програмістів. Ось один класичний приклад:

if if .eq. then then = else else else = endif endif
K  I   K   K    I      I    K    I      I     K

Тут я позначив "ключові слова мови" за допомогою K та ідентифікаторів (імен змінних) I. З огляду на те, що в написанні немає різниці, я думаю, ви, напевно, можете зрозуміти, наскільки це може бути заплутаним. Звичайно, це надзвичайний приклад, і навряд чи хтось колись писав подібний код спеціально. Іноді люди робили «Recycle» мову ключові слова в якості імен ідентифікаторів , хоча - і в більшості випадків просто помилка може призвести до коду , що мова специфікацією вказаною повинен бути проаналізований таким чином, навіть якщо вона не призначалася зовсім. Для іншого відомого прикладу порівняйте це:

do 10 i = 1,10

до цього:

do 10 i = 1.10

Перший - цикл do - повторення блоку коду 10 разів. У другому, однак, кома була змінена на десяткову точку, тому вона присвоює значення 1.10змінній з назвою do 10 i.

Це також означало, що написати парсер Fortran порівняно складно - ви не могли бути впевнені, що doна початку рядка було дійсно ключовим словом, поки ви не дійшли до кінця рядка, і не підтвердили, що всі інші елементи doпетля була присутня. Парзатор, як правило, повинен був бути готовим "відступити", повторно розбираючи рядок від початку, щоб дійти до "правильної" (але часто ненавмисної) відповіді на те, що насправді є.

Через кілька років цього дизайнери мови (більшість із них все одно) пішли в протилежну крайність - максимально обмеживши майже все про мову, не надто нарікаючи на користувачів .

Наприклад, раніше BASIC, в основному, сказав, що ви навіть не можете використовувати ключове слово як частину ідентифікатора - наприклад, воно fora=1буде розбиратися як for a = 1(тобто початок forциклу, а не призначення). Це, мабуть, породило достатньо скарг, що це тривало не дуже довго. Правило про запуск ідентифікатора цифрою, мабуть, не викликало багато скарг, тому воно продовжує використовуватися (принаймні, на більшості мов).


ІМХО це найближче до реальної причини. Ранні мови, такі як Fortran, були в деякій мірі занадто неструктурованими, що призводило до труднощів із написанням надійних компіляторів і до труднощів для візуального розбору вихідного коду для людей. "Do10i = ..." - класичний і відомий приклад. З розвитком мов деякі правила були жорсткішими. Алгол - це, мабуть, дідусь із стандарту "ідентифікатори починаються з літер, а потім можуть мати літери або цифри".
Ларрі Гріц

FYI, інтерпретатор Microsoft BASIC, який лягли в основу найпопулярніших версій мікрокомп'ютерів BASIC (включаючи Applesoft Basic і Commodore Basic), використовував жадібний токенізатор для перетворення будь-якої послідовності символів, які відповідали мовній токені, у байтове значення з високим набором біт. Це було зроблено без будь-якого синтаксичного аналізу. Тоді, запускаючи програму, інтерпретатор вважає, що будь-які букви, які вона знайшла, є частиною імені змінної.
supercat

1

Ймовірно, ця конвенція склалася з дуже ранніх дизайнерських рішень історичної мови, оскільки на ранніх машинах весь компілятор, включаючи лексичний аналіз, повинен був працювати в декілька kWords, менше пам'яті, ніж навіть просто кеш даних кеш-даних першого рівня на поточних мобільних пристроях, тому дозволені назви змінних були дуже обмеженими, і їх було легко відрізнити від числових констант у дуже мало оп-кодах.

Таким чином, конвенція стала тим, до чого звикли покоління програмістів.


1

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

Я можу спроектувати різну мову, яка дозволяє всім символам для ідентифікаторів. Для всіх рядків коду перший 20 символів описує тип висловлювання, потім наступний 20 символів визначатиме перший символ для оператора, а наступний 20 символів - операнд для оператора. Ця мова буде виконана на стековому процесорі.

01234567890123456789 01234567890123456789 01234567890123456789

decl symbol          12345                
assign value         12345                12345
decl symbol          99999                
assign value         99999                12345
push                 12345
push                 99999
add
print top

Цей код можна перекласти на C, як показано нижче:

int i12345 = 12345;
int i99999 = 12345;
printf("%d", i12345+i9999);

Це все. Це безглуздо, і правило без ідентифікаторів також не має сенсу логічного підґрунтя.


0

Окрім "зручності для лексеру", я думаю, що варто також розглянути "зручність для читача".

Читаючи код, потрібно швидко і неодноразово визначати, які слова є ідентифікаторами, а які - числами. Шукати цифру на початку простіше на нашому візуальному зіставленні шаблону; була б справа, якщо нам доведеться уважно перевірити всіх персонажів, щоб переконатися.


0

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

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