¿Por qué los identificadores no deben comenzar con un número?

32

La mayoría de los lenguajes de programación parecen estar diseñados para no permitir que uno declare un identificador que comienza con un número. Tenía curiosidad por saber la razón. Ya he buscado en la web, pero no pude encontrar una explicación satisfactoria.

Sivasubramaniam Arunachalam
fuente
44
¿Tiene un solo ejemplo de un nombre de variable donde tendría un beneficio para la claridad y la legibilidad?
Seguro
55
@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...
usuario desconocido
66
Forth lo permite. De los incorporados: 2DUP, 2DROP, 2SWAP, 2> R, 2R @, 2R>, 0 =, etc.
Peter Mortensen
al igual que TCL, pero no creo que ninguno de los comandos TCL estándar comience con un número
jk.
1
Posible lo mismo en SO: stackoverflow.com/questions/342152/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Respuestas:

51

En C / C ++, un número seguido de una letra se considera una constante numérica y la cadena que sigue califica el tipo de la constante. Entonces, por ejemplo (estos son VC ++, no estoy seguro de qué tan estándar son):

  • 0 - entero con signo
  • 0l - entero largo firmado
  • 0u - entero sin signo
  • 0i64 - entero con signo de 64 bits

Entonces a) es más fácil para el lexer como dijo Daniel, pero también b) hace una distinción explícita ya que 0y podría ser una variable pero 0u nunca lo sería. Además, otros calificadores, como "i64", se agregaron mucho más tarde que "l" o "u" y quieren mantener abierta la opción de agregar más si es necesario.

DXM
fuente
77
Además, los números hexadecimales se escriben en la forma 0xd + donde d + son 1 dígitos hexadecimales más 0-f, por lo que 0xbeef es un "número" perfectamente válido.
tcrosley
20
ustedes se dan cuenta de que no estaba buscando una especificación de idioma, pero solo proporcionaron algunos ejemplos para ilustrar el punto, ¿verdad?
DXM
66
Re: "quieren mantener abierta la opción de agregar más si es necesario": Y C ++ 11 incluso te permite agregar la tuya propia; ver http://en.wikipedia.org/wiki/C++11#User-defined_literals .
ruakh
2
No creo que esta sea la explicación correcta. La regla "el identificador no puede comenzar con un dígito" era verdadera para Algol, Pascal y otros idiomas que no permitían sufijos alfabéticos a constantes numéricas.
Larry Gritz
1
@LarryGritz: "La separación constante de palabras por espacios se convirtió en una costumbre general sobre el siglo X dC, y duró hasta aproximadamente 1957, cuando FORTRAN abandonó la práctica". —Sun FORTRAN Manual de referencia (de wiki). Fortran tenía sus propias razones especiales porque decidieron que los espacios en general eran opcionales. Idiomas modernos como su espacio en blanco. Estás solo con Algol, pero tampoco soy tan moderno. Por otro lado, C / C ++ / C # / F # tienen sufijos.
DXM
49

La conveniencia de las personas que implementan el lexer. (No, en serio, de eso se trata. Varios idiomas tienen otras razones, pero finalmente se reduce a eso).

Daniel Pittman
fuente
2
Sería fácil distinguir entre literales integrales e identificadores que comienzan con dígitos utilizando PEG u otras técnicas modernas de análisis. Incluso los compiladores que usan lexers primitivos podrían ponerlos en la misma categoría de tokens y diferenciarlos más tarde. Sería muy incómodo si, por ejemplo, 0flufuera un literal y 0gluun identificador local.
Daniel Lubarov
2
Es absolutamente posible que las personas los distingan. La decisión se toma en base a la conveniencia (o, si es menos caritativo, la pereza) en lugar de los requisitos técnicos.
Daniel Pittman
2
@DanielPittman: Necesitaría un análisis semántico para hacer cualquier tipo de desambiguación confiable, por lo que no se puede hacer en el lexer. Sacar la decisión del lexer hace que el analizador sea más complejo y ¿para qué beneficio? Además de la muy mala situación de costo / beneficio, simplemente no hay una buena manera de manejar un caso como int 0u = 5; unsigned int x = 0u;Sin embargo, usted elige definir la interpretación de este código (probablemente x == 0 o x == 5), las personas se confundirán por la ambigüedad Incluso si fuera trivial implementar el compilador de esta manera, un buen diseñador probablemente no lo haría.
Joren
10
La conveniencia principal es para el analizador en mi cabeza, y no para el creador del lenguaje.
CodesInChaos
2
Todavía es una sorpresa para muchas personas saber que el análisis léxico suele ser un factor importante en la etapa más lenta de un compilador / intérprete.
hippietrail
20

Considere los siguientes 2 casos:

Caso 1

Supongamos que un identificador podría comenzar con un número.

Por lo tanto, una declaración como la siguiente sería válida (ya que un identificador puede tener 1 o más caracteres):

int 3;

Cuando trato de usar la variable anterior en un programa, resultará en la ambigüedad del compilador:

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

En el enunciado, a=3¿cuál es el papel de 3 (¿es una variable con valor 5 o es el número 3)?

Caso 2

A diferencia del ejemplo anterior, supongamos que un idioma permitiría en realidad identificadores que comiencen con un número sin permitir que los números se usen como identificadores. Esto puede causar los siguientes problemas:

  • Las reglas del lenguaje con respecto a la variable que dice que una variable puede constar de 1 o más caracteres deberán redefinirse a una regla compleja como: Una variable puede tener uno o más caracteres y debe ser única si no comienza con un número mientras no puede tener una longitud de un solo carácter al comenzar con un número (etc.)

  • El compilador tendrá que verificar y reportar casos de error cuando todos los números (ej. 333) y sufijos válidos del alfabeto (ej. 34L) se estén utilizando como nombres de variables. En lenguajes poco escritos como Python y JS donde puede usar variables sobre la marcha sin declararlas, incluso puede ser imposible verificar los casos especiales que involucran todos los números, por ejemplo if (33==5), Aquí, 33 podría ser una variable errónea no declarada que el usuario ha declarado. Pero el compilador no podrá identificar esto e informar el error.

Hacer esta restricción evitará que el programador use números como nombres de identificación.

aml90
fuente
2
Bajo esta lógica, los identificadores no pueden contener caracteres, ya que serían ambiguos a las palabras clave. ¿Te imaginas lo desastroso int char = floatque sería?
Pubby
44
@Pubby: No veo cómo puedes extrapolar lo que dije a un sinsentido que aún no puedo entender. ¿Qué significa tu comentario?
aml90
Estoy diciendo que está tomando la pregunta demasiado literalmente y que no es del todo ambigua al usar la precedencia lexing. Por ejemplo, ¿cómo sabe el compilador que intes una palabra clave y no un identificador? Bueno, inttiene mayor precedencia tal como lo tendrían los lexemas numéricos.
Pubby
@Pubby: Por ambigüedad quise decir que el compilador no sabría en qué contexto estoy usando el nombre de la variable (incluso usando la precedencia léxica). Por ejemplo, considere este código: int 3,a; 3=5; a=3; en el enunciado a = 3, ¿se interpreta 3 como un identificador o como un número? Esto causa ambigüedad. Espero que esté claro.
aml90
2
También encuentro este argumento débil. Sería trivial escribir un lexer que acepte identificadores que comiencen con, pero no estén completamente compuestos, de números.
Larry Gritz
11

En su mayor parte, esto no tiene nada que ver con facilitar a los escritores del compilador y la eficacia del análisis, sino más bien con el diseño de una sintaxis que fomente un código claro, legible y sin ambigüedades.

Los diseñadores de lenguaje pensaron que sería bueno poder escribir literales numéricos como el número 1 como simplemente 1 .

Sería bastante posible diseñar una sintaxis de lenguaje donde los literales numéricos se citaran de alguna manera, por ejemplo tildas, de modo que el literal numérico para el número uno se codificara como ~ 1 ~ y cualquier cosa que no fuera una palabra clave y no se incluyera entre comillas se tratara como un nombre variable .

Para que pueda codificar declaraciones como:

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

Pero también:

2 = ~3~
six = 2 + 2

Cualquiera sea la sintaxis que elija, el código ambiguo y difícil de seguir es inevitable.

El lenguaje C y la mayoría de los lenguajes de "llaves" descendientes de C también consideraron una buena idea permitir que los programadores codifiquen los literales Octal y Hexadecimal directamente, y especificar el tipo del literal si esto fuera importante. Asi que

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

Entonces, incluso si permitía que los nombres de variables comenzaran con un número seguido de una combinación de números y letras que incluía al menos una letra, le presentaría al programador el problema de decidir si un grupo dado formó un nombre de variable o un literal numérico

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

Tal ambigüedad no ayudaría a nadie a escribir o leer un programa.

Para ver un ejemplo del mundo real estrechamente relacionado, podría ver el lenguaje PL / 1 cuyos diseñadores pensaron que poder usar palabras clave como nombres de variables era una buena idea para que:

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

Es un código válido que compila y ejecuta.

James Anderson
fuente
C fue diseñado como ensamblaje portátil para Unix. Unix fue diseñado originalmente para una máquina de 18 bits, donde octal es una buena opción para imprimir de la misma manera que hex es una buena opción para imprimir valores de máquina de 8/16/32 bits. Por lo tanto, en realidad necesitaban octal.
También para el giro de bits (OR, XOR, AND, NOT) y la implementación de controladores de dispositivos, ¡es importante especificar el tamaño exacto de un literal y el valor!
James Anderson
10

Fortran tuvo un gran efecto en cómo se diseñaron los idiomas posteriores. Al principio (algunos de estos problemas se han solucionado desde entonces) Fortran casi no tenía reglas que restringieran el nombre que se le podía dar a un identificador. Esto hizo que el lenguaje fuera extremadamente difícil de analizar tanto para los compiladores como para los programadores. Aquí hay un ejemplo clásico:

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

Aquí he marcado las "palabras clave del lenguaje" con K y los identificadores (nombres de variables) I. Dado que no hay diferencia en la ortografía, creo que probablemente puedas entender lo confuso que puede ser. Por supuesto, este es un ejemplo extremo, y es poco probable que alguien haya escrito un código como este a propósito. Sin embargo, a veces las personas "reciclaban" las palabras clave del idioma como nombres de identificación, y en muchos casos un error tipográfico simple podía generar un código que la especificación del lenguaje decía que debería analizarse de esta manera, aunque no fuera para nada. Para otro ejemplo bien conocido, compare esto:

do 10 i = 1,10

a esto:

do 10 i = 1.10

El primero es un bucle do: iterar un bloque de código 10 veces. El segundo, sin embargo, ha cambiado la coma a un punto decimal, por lo que está asignando el valor 1.10a una variable llamada do 10 i.

Esto también significaba que escribir un analizador Fortran era relativamente difícil: no se podía estar seguro de que doal principio de la línea era realmente una palabra clave hasta llegar al final de la línea y verificar que todos los demás elementos de un doloop estaban presentes. El analizador generalmente tenía que estar listo para "retroceder", volviendo a analizar la línea desde el principio para llegar a la respuesta "correcta" (pero a menudo involuntaria) de lo que realmente estaba allí.

Después de unos años de esto, los diseñadores de idiomas (la mayoría de ellos de todos modos) se dirigieron al extremo opuesto, restringiendo casi todo lo posible sobre el idioma sin que los usuarios se quejen demasiado .

Early BASIC, por ejemplo, básicamente decía que ni siquiera podía usar una palabra clave como parte de un identificador; por ejemplo, fora=1se analizaría como for a = 1(es decir, el comienzo de un forbucle, no una asignación). Al parecer, eso generó suficientes quejas de que no duró mucho. La regla sobre comenzar un identificador con un dígito aparentemente no ha generado muchas quejas, por lo que continúa utilizándose (al menos en la mayoría de los idiomas).

Jerry Coffin
fuente
En mi humilde opinión esto es más cercano a la verdadera razón. Los primeros lenguajes como Fortran eran, de alguna manera, demasiado desestructurados, lo que generaba dificultades para escribir compiladores robustos y dificultades para que los humanos analizaran visualmente correctamente el código fuente. El "do10i = ..." es un ejemplo clásico y famoso. A medida que los idiomas evolucionaron, algunas de las reglas se endurecieron. Algol es probablemente el abuelo de la norma "identificadores que comienzan con letras y luego pueden tener letras o números" como regla general.
Larry Gritz
Para su información, el intérprete de Microsoft BASIC que formó la base de las versiones más populares de BASIC para microcomputadoras (incluidas Applesoft Basic y Commodore Basic) utilizó un tokenizador codicioso para convertir cualquier secuencia de caracteres que coincidiera con un token de idioma en un valor de byte con el conjunto de bits alto. Esto se hizo sin ningún análisis sintáctico. Luego, al ejecutar el programa, el intérprete asumirá que las letras que encontró constituyen parte de un nombre de variable.
supercat
1

Probablemente, esta convención ha evolucionado a partir de decisiones de diseño de lenguaje histórico muy tempranas, ya que en las primeras máquinas todo el compilador, incluido el análisis léxico, tenía que ejecutarse en unos pocos kWords, menos memoria que incluso el caché de datos del procesador de primer nivel en los dispositivos móviles actuales, por lo tanto, los nombres de variables permitidos eran muy limitados y tenían que ser fáciles de distinguir de las constantes numéricas en muy pocos códigos operativos.

Por lo tanto, la convención se convirtió en lo que las generaciones de programadores están acostumbrados.

hotpaw2
fuente
1

No es una regla requerida lógicamente para el lenguaje de programación, sino solo la convención utilizada por muchos diseñadores de lenguaje.

Puedo diseñar un lenguaje radicalmente diferente que permita todos los caracteres para los identificadores. Para todas las líneas de código, los primeros 20 caracteres describirán el tipo de declaración, luego los siguientes 20 caracteres definirán el primer símbolo para la declaración, y los siguientes 20 caracteres son operandos para la declaración. Este lenguaje se ejecutará en un procesador de pila.

01234567890123456789 01234567890123456789 01234567890123456789

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

Este código podría traducirse en C de la siguiente manera:

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

Eso es todo. No tiene sentido y la regla de no-número-en-identificadores tampoco tiene sentido en un terreno lógico.

9dan
fuente
0

Además de "conveniencia para el lexer", creo que también vale la pena considerar "conveniencia para el lector".

Al leer el código, debe identificar rápida y repetidamente qué palabras son identificadores y cuáles son números. Buscar un dígito al principio es más fácil en nuestra coincidencia visual de patrones; sería una tarea ardua si tuviéramos que revisar cuidadosamente todos los personajes para asegurarnos.

tormenta
fuente
0

La respuesta a esta pregunta radica en autómatas o, más precisamente, autómatas finitos que definen la expresión regular. La regla es ... los compiladores necesitan algoritmos o reglas exactas para decidir en cada personaje que analizan. Si se permitiera que los identificadores comenzaran con un número, entonces el compilador estará en una solución ... sobre la naturaleza del token que viene ... será un número o un identificador ... y como los compiladores no pueden retroceder a posiciones anteriores ... .so..para dejar en claro al compilador que el token que viene es precisamente un identificador o un número ... esta restricción está ahí ... porque de esto ... el compilador sabe simplemente escaneando el primer carácter que el token que viene es un identificador o un número.

Waquas
fuente