Похоже, что большинство языков программирования не позволяют объявлять идентификатор, начинающийся с цифры. Мне было просто интересно узнать причину. Я уже искал в Интернете, но не смог найти удовлетворительного объяснения.
Похоже, что большинство языков программирования не позволяют объявлять идентификатор, начинающийся с цифры. Мне было просто интересно узнать причину. Я уже искал в Интернете, но не смог найти удовлетворительного объяснения.
Ответы:
В 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;
а = 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 и большинство языков "фигурных скобок", происходящих от 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
.
Это также означало, что написание синтаксического анализатора на Фортране было относительно сложно - вы не могли быть уверены, что do
в начале строки действительно было ключевое слово, пока вы не достигли конца строки, и проверили, что все остальные элементы do
петли присутствовали. Обычно синтаксический анализатор должен был быть готов к «возврату», заново анализируя строку с самого начала, чтобы найти «правильный» (но часто непреднамеренный) ответ о том, что действительно было.
Через несколько лет разработчики языка (большинство из них в любом случае) пошли к противоположной крайности - максимально ограничивали почти все в языке, при этом пользователи не жаловались слишком сильно.
В раннем Бейсике, например, в основном говорилось, что вы даже не можете использовать ключевое слово в качестве части идентификатора - например, fora=1
будет анализироваться как for a = 1
(т. Е. Начало for
цикла, а не присвоение). Это, очевидно, вызвало достаточно жалоб, что это длилось не очень долго. Правило о начале идентификатора с цифрой, по-видимому, не вызвало большого количества жалоб, поэтому оно продолжает использоваться (по крайней мере, в большинстве языков).
Вероятно, это соглашение возникло из очень ранних исторических решений по проектированию языка, поскольку на ранних машинах весь компилятор, включая лексический анализ, должен был работать за несколько киловатт-часов, меньше памяти, чем даже просто кэш данных процессора первого уровня на современных мобильных устройствах, поэтому допустимые имена переменных были очень ограничены, и их было легко отличить от числовых констант в очень немногих кодах операций.
Таким образом, соглашение стало тем, к чему привыкли поколения программистов.
Это не логически обязательное правило для языка программирования, а просто соглашение, используемое многими разработчиками языка.
Я могу разработать совершенно другой язык, который позволяет использовать все символы для идентификаторов. Для всех строк кода первые 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);
Вот и все. Это бессмысленно, и правило «без числа в идентификаторах» также бессмысленно с логической точки зрения.
В дополнение к «удобству для лексера», я думаю, стоит также рассмотреть «удобство для читателя».
При чтении кода необходимо быстро и многократно определить, какие слова являются идентификаторами, а какие - числами. Поиск цифры в начале легче в нашем визуальном сопоставлении с образцом; это было бы рутиной, если бы нам нужно было тщательно проверить всех персонажей, чтобы убедиться.
Ответ на этот вопрос лежит в автоматах или, точнее, в конечных автоматах, определяющих регулярное выражение. Правило ... компиляторам нужны точные алгоритмы или правила для определения каждого символа, который они анализируют. Если идентификаторы были разрешены для начала с числа, то компилятор будет исправлен ... о характере приходящего токена ... будет ли это число или идентификатор ... и так как компиляторы не смогут вернуться к более ранним позициям .. .so .. чтобы дать понять компилятору, что приходящий токен является в точности идентификатором или числом ... это ограничение есть ... потому что это ... компилятор знает, просто сканируя первый символ, который входит в токен является идентификатором или номером.