Здається, більшість мов програмування розроблені так, щоб не дозволяти оголошувати ідентифікатор, який починається з числа. Мені просто цікаво було знати причину. Я вже шукав в Інтернеті, але не зміг знайти задовільне пояснення.
Здається, більшість мов програмування розроблені так, щоб не дозволяти оголошувати ідентифікатор, який починається з числа. Мені просто цікаво було знати причину. Я вже шукав в Інтернеті, але не зміг знайти задовільне пояснення.
Відповіді:
У C / C ++ число, за яким йде буква, вважається числовою константою, а рядок, що випливає, кваліфікує тип константи. Так, наприклад (це VC ++, не впевнені, наскільки вони стандартні):
Отже, а) лексеру легше, як сказав Даніель, але також б) він чітко розрізняє, оскільки 0y може бути змінною, але 0u ніколи не буде. Плюс інші класифікатори, такі як "i64", додаються набагато пізніше, ніж "l" або "u", і вони хочуть залишати опцію відкритою, додаючи більше, якщо потрібно.
Зручність людей, що реалізують лексеру. (Ні, серйозно, це стосується цього. У різних мов є й інші причини, але це зрештою зводиться до цього.)
0flu
було буквальним і 0glu
було локальним ідентифікатором.
int 0u = 5; unsigned int x = 0u;
однак, ви вирішите визначити тлумачення цього коду (ймовірно, x == 0 або x == 5), люди будуть плутати через неоднозначність. Навіть якби банально реалізувати компілятор таким чином, хороший дизайнер цього, швидше за все, не зробив би.
Розглянемо наступні 2 випадки:
Припустимо, що ідентифікатор може починатися з числа.
Отже, твердження, як показано нижче, буде дійсним (оскільки ідентифікатор може мати 1 або більше символів):
int 3;
Коли я намагаюся використовувати вищезазначену змінну в програмі, це призведе до неоднозначності компілятора:
int 3, a;
3 = 5;
a = 3;
У твердженні a=3
яка роль 3 (це змінна зі значенням 5 чи це цифра 3)?
На відміну від наведеного вище прикладу, давайте припустимо, що мова фактично повинна дозволяти ідентифікаторам, починаючи з числа, в той час як забороняється використовувати цифри як ідентифікатори. Це може спричинити такі проблеми:
Мовні правила щодо змінної, яка говорить про те, що змінна може складатися з 1 або більше символів, доведеться переглядати на складне правило типу: Змінна може мати один або більше символів і повинна бути унікальною, якщо вона не починається з числа вона не може бути однієї символьної довжини, починаючи з числа (тощо).
Компілятору доведеться перевіряти і повідомляти про випадки помилок, коли всі цифри (наприклад, 333) та дійсні суфікси алфавіту (наприклад, 34L) використовуються як імена змінних. У слабко типізованих мовах, таких як Python та JS, де ви можете використовувати змінні на льоту, не оголошуючи їх, можливо, навіть неможливо перевірити особливі випадки, що включають усі цифри, наприклад if (33==5)
, 33 тут може бути помилковою незадекларованою змінною, яку оголосив користувач. Але компілятор не зможе це визначити та повідомити про помилку.
Здійснення цього обмеження перешкоджає програмісту використовувати числа як імена ідентифікаторів.
int char = float
було б?
int
, що це ключове слово, а не ідентифікатор? Що ж, int
має більшу перевагу, як і чисельні лексеми.
int 3,a; 3=5; a=3;
У твердженні a = 3 інтерпретується 3 як ідентифікатор або як число? Це викликає неоднозначність. Сподіваюся, це зрозуміло.
Здебільшого це не має нічого спільного з тим, щоб полегшити розробникам авторів-компіляторів та аналізувати ефективність, але, більше, це стосується розробки синтаксису, який заохочує чіткий читабельний та однозначний код.
Мовні дизайнери вважали, що було б непогано мати можливість записати числові літери, як число 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;
Дійсний код, який компілює та виконує.
Фортран мав величезний вплив на те, як розроблялися пізніші мови. На початку (деякі з цих проблем були усунені) у 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
циклу, а не призначення). Це, мабуть, породило достатньо скарг, що це тривало не дуже довго. Правило про запуск ідентифікатора цифрою, мабуть, не викликало багато скарг, тому воно продовжує використовуватися (принаймні, на більшості мов).
Ймовірно, ця конвенція склалася з дуже ранніх дизайнерських рішень історичної мови, оскільки на ранніх машинах весь компілятор, включаючи лексичний аналіз, повинен був працювати в декілька kWords, менше пам'яті, ніж навіть просто кеш даних кеш-даних першого рівня на поточних мобільних пристроях, тому дозволені назви змінних були дуже обмеженими, і їх було легко відрізнити від числових констант у дуже мало оп-кодах.
Таким чином, конвенція стала тим, до чого звикли покоління програмістів.
Це не логічно необхідне правило для мови програмування, а лише умова, яку використовують багато мовних дизайнерів.
Я можу спроектувати різну мову, яка дозволяє всім символам для ідентифікаторів. Для всіх рядків коду перший 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);
Це все. Це безглуздо, і правило без ідентифікаторів також не має сенсу логічного підґрунтя.
Окрім "зручності для лексеру", я думаю, що варто також розглянути "зручність для читача".
Читаючи код, потрібно швидко і неодноразово визначати, які слова є ідентифікаторами, а які - числами. Шукати цифру на початку простіше на нашому візуальному зіставленні шаблону; була б справа, якщо нам доведеться уважно перевірити всіх персонажів, щоб переконатися.
Відповідь на це запитання лежить у автоматах або, точніше, кінцевих автоматах, що визначають регулярний вираз. Правило полягає в тому, що ... компіляторам потрібні точні алгоритми або правила, щоб вирішити кожен символ, який вони аналізують. Якщо ідентифікатори дозволили розпочати з числа, компілятор буде виправлено ... про характер надходження маркера ... чи буде це число чи ідентифікатор ... і як компілятори не можуть повернутись до попередніх позицій .. .так ... щоб дати зрозуміти компілятору, що надходить маркер - це саме ідентифікатор або число ... це обмеження існує ... тому що цей ... компілятор знає лише скануючи перший символ, що надходить маркер - це ідентифікатор або число.