Pourquoi les identifiants ne devraient-ils pas commencer par un chiffre?

32

La plupart des langages de programmation semblent être conçus pour ne pas permettre de déclarer un identifiant commençant par un nombre. J'étais simplement curieux de connaître la raison. J'ai déjà cherché sur le Web, mais je n'ai pas trouvé d'explication satisfaisante.

Sivasubramaniam Arunachalam
la source
4
Avez-vous un seul exemple de nom de variable où il aurait un avantage pour la clarté et la lisibilité?
Sécurisé le
5
@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...
utilisateur inconnu
6
Forth le permet. Des modules intégrés: 2DUP, 2DROP, 2SWAP, 2> R, 2R @, 2R>, 0 =, etc.
Peter Mortensen
tout comme TCL, mais je ne pense pas que les commandes TCL standard commencent par un nombre
jk.
1
Même chose possible sur SO: stackoverflow.com/questions/342152/…
Ciro Santilli 新疆 改造 中心 法轮功 六四 事件

Réponses:

51

En C / C ++, un nombre suivi d'une lettre est considéré comme une constante numérique et la chaîne qui suit qualifie le type de la constante. Ainsi, par exemple (ce sont VC ++, je ne sais pas à quel point ils sont standard):

  • 0 - entier signé
  • 0l - entier long signé
  • 0u - entier non signé
  • 0i64 - Entier signé 64 bits

Donc a) c'est plus facile pour le lexer comme Daniel l'a dit mais aussi b) ça fait une distinction explicite puisque 0y pourrait être une variable mais 0u ne le serait jamais. De plus, d'autres qualificatifs, comme «i64», ont été ajoutés bien plus tard que «l» ou «u» et ils veulent garder l'option ouverte d'en ajouter plus si nécessaire.

DXM
la source
7
aussi, les nombres hexadécimaux sont écrits sous la forme 0xd + où d + est 1 chiffre hexadécimal de plus 0-f - donc 0xbeef est un "nombre" parfaitement valide.
tcrosley
20
vous vous rendez compte que je n'allais pas pour une spécification de langue, mais je n'ai fourni que quelques exemples pour illustrer ce point, non?
DXM
6
Re: "ils veulent garder l'option ouverte d'ajouter plus si nécessaire": Et C ++ 11 vous permet même d'ajouter le vôtre; voir http://en.wikipedia.org/wiki/C++11#User-defined_literals .
ruakh
2
Je ne pense pas que ce soit la bonne explication. La règle "l'identifiant ne peut pas commencer par un chiffre" était vraie pour Algol, Pascal et d'autres langues qui ne permettaient pas les suffixes alphabétiques aux constantes numériques.
Larry Gritz
1
@ LarryGritz: "Séparer constamment les mots par des espaces est devenu une coutume générale au Xe siècle après JC, et a duré jusqu'en 1957, lorsque FORTRAN a abandonné la pratique." —Sun FORTRAN Reference Manual (à partir du wiki). Fortran avait ses propres raisons spéciales, car ils décidaient que les espaces en général étaient facultatifs. Les langues MODERNES aiment leur espace. Vous êtes seul avec Algol, mais je ne suis pas aussi moderne que celui-ci. En revanche, C / C ++ / C # / F # ont tous des suffixes.
DXM
49

La commodité des personnes implémentant le lexer. (Non, sérieusement, c'est à ce sujet. Diverses langues ont d'autres raisons, mais cela se résume finalement à cela.)

Daniel Pittman
la source
2
Il serait facile de faire la distinction entre les littéraux intégraux et les identifiants commençant par des chiffres à l'aide de PEG ou d'autres techniques d'analyse modernes. Même les compilateurs utilisant des lexers primitifs pourraient les mettre dans la même catégorie de jetons et les différencier plus tard. Ce serait juste très gênant si par exemple 0fluétait un littéral et 0gluun identifiant local.
Daniel Lubarov
2
Il est absolument possible pour les gens de les distinguer. La décision est prise en fonction de la commodité (ou, si vous êtes moins charitable, de la paresse) plutôt que des exigences techniques.
Daniel Pittman
2
@DanielPittman: Vous auriez besoin d'une analyse sémantique pour faire toute sorte de désambiguïsation fiable, donc cela ne peut pas être fait dans le lexer. Pousser la décision hors du lexer rend l'analyseur plus complexe, et à quel avantage? Outre la très mauvaise situation coûts / avantages, il n'y a tout simplement pas de bon moyen de gérer un cas comme int 0u = 5; unsigned int x = 0u;Cependant, vous choisissez de définir l'interprétation de ce code (probablement x == 0 ou x == 5), les gens vont être confus à cause de l'ambiguïté. Même s'il était trivial d'implémenter le compilateur de cette façon, un bon concepteur ne le ferait probablement pas.
Joren
10
Le principal avantage est pour l'analyseur dans ma tête, et non pour le créateur de la langue.
CodesInChaos
2
C'est toujours une surprise pour beaucoup de gens d'apprendre que l'analyse lexicale est généralement un facteur important de l'étape la plus lente d'un compilateur / interprète.
hippietrail
20

Considérez les 2 cas suivants:

Cas 1

Supposons qu'un identifiant puisse commencer par un nombre.

Une déclaration comme ci-dessous serait donc valide (puisqu'un identifiant peut avoir 1 ou plusieurs caractères):

int 3;

Lorsque j'essaie d'utiliser la variable ci-dessus dans un programme, cela entraîne une ambiguïté du compilateur:

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

Dans l'énoncé, a=3quel est le rôle de 3 (est-ce une variable de valeur 5 ou est-ce le chiffre 3)?

Cas 2

Contrairement à l'exemple ci-dessus, supposons qu'une langue devait réellement autoriser les identifiants commençant par un nombre tout en interdisant l'utilisation de chiffres comme identifiants. Cela peut provoquer les problèmes suivants:

  • Les règles de langage concernant la variable qui dit qu'une variable peut comprendre 1 ou plusieurs caractères devront être redéfinies en une règle complexe comme: Une variable peut avoir un ou plusieurs caractères et doit être unique si elle ne commence pas par un nombre tandis que il ne peut pas avoir une longueur de caractère unique en commençant par un nombre (etc.)

  • Le compilateur devra vérifier et signaler les cas d'erreur lorsque tous les chiffres (par exemple 333) et les suffixes d'alphabet valides (par exemple 34L) sont utilisés comme noms de variables. Dans des langages mal typés comme Python et JS où vous pouvez utiliser des variables à la volée sans les déclarer, il peut même être impossible de vérifier les cas spéciaux impliquant tous les chiffres, par exemple if (33==5)ici, 33 pourrait être une variable non déclarée erronée que l'utilisateur a déclarée. Mais le compilateur ne pourra pas identifier cela et signaler l'erreur.

Cette restriction empêchera le programmeur d'utiliser des nombres comme noms d'identificateurs.

aml90
la source
2
Dans cette logique, les identifiants ne pouvaient pas contenir de caractères car ils seraient ambigus par rapport aux mots clés. Pouvez-vous imaginer à quel point ce int char = floatserait désastreux ?
Pubby
4
@Pubby: Je ne vois pas comment extrapoler ce que j'ai dit à un non-sens absolu que je ne peux pas encore comprendre. Que signifie votre commentaire?
aml90
Je dis que vous prenez la question trop à la lettre et qu'elle n'est pas du tout ambiguë en utilisant la priorité lexicale. Par exemple, comment le compilateur sait-il qu'il ints'agit d'un mot-clé et non d'un identifiant? Eh bien, inta une priorité plus élevée, tout comme les lexèmes numériques.
Pubby
@Pubby: Par ambiguïté, je voulais dire que le compilateur ne saurait pas dans quel contexte j'utilise le nom de variable (même en utilisant la priorité lexicale). Par exemple, considérons ce code: int 3,a; 3=5; a=3; Dans l'énoncé a = 3, 3 est-il interprété comme un identifiant ou comme un nombre? Cela crée une ambiguïté. J'espère que c'est clair.
aml90
2
Je trouve également cet argument faible. Il serait trivial d'écrire un lexer qui accepterait des identifiants commençant par, mais n'étant pas entièrement composés de chiffres.
Larry Gritz
11

Pour la plupart, cela n'a rien à voir avec la facilité pour les rédacteurs du compilateur et l'efficacité de l'analyse, mais plutôt avec la conception d'une syntaxe qui encourage un code clair et lisible et sans ambiguïté.

Ses concepteurs de langage ont pensé qu'il serait bien de pouvoir écrire des littéraux numériques comme le chiffre 1 comme tout simplement 1 .

Il serait tout à fait possible de concevoir une syntaxe de langage dans laquelle les littéraux numériques seraient cités d'une certaine manière, par exemple les tildas, de sorte que le littéral numérique pour le numéro un serait codé comme ~ 1 ~ et tout ce qui n'est pas un mot-clé et non entre guillemets serait traité comme un nom de variable .

Vous pouvez donc coder des instructions comme:

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

Mais aussi:

2 = ~3~
six = 2 + 2

Quelle que soit la syntaxe que vous choisissez, le code ambigu et difficile à suivre est inévitable.

Le langage C et la plupart des langages "accolades" issus de C pensaient également que c'était une bonne idée de permettre aux programmeurs de coder directement les littéraux octal et hexadécimal, et de spécifier le type du littéral si cela était important. Alors

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

Donc, même si vous autorisez les noms de variables à commencer par un nombre suivi d'une combinaison de chiffres et de lettres qui comprenait au moins une lettre, vous poseriez au programmeur le problème de décider si un groupe donné formait un nom de variable ou un littéral numérique afin

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

Une telle ambiguïté n'aiderait personne à écrire ou à lire un programme.

Pour un exemple proche du monde réel, vous pouvez regarder le langage PL / 1 dont les concepteurs pensaient que pouvoir utiliser des mots clés comme noms de variable était une bonne idée, de sorte 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;

Est un code valide qui compile et exécute.

James Anderson
la source
C a été conçu comme un assemblage portable pour Unix. Unix a été initialement conçu pour une machine 18 bits, où octal est un bon ajustement pour imprimer de la même manière que hex est un bon ajustement pour imprimer des valeurs de machine 8/16/32 bits. Par conséquent, ils avaient réellement besoin d'octal.
Aussi pour le twiddling de bits (OR, XOR, AND, NOT) et la mise en œuvre de pilotes de périphériques, il est important de spécifier la taille exacte d'un littéral ainsi que la valeur!
James Anderson
10

Fortran a eu un effet énorme sur la conception des langages ultérieurs. Très tôt (certains de ces problèmes ont depuis été corrigés), Fortran n'avait pratiquement pas de règles limitant le nom que vous pouviez donner à un identifiant. Cela a rendu le langage extrêmement difficile à analyser à la fois pour les compilateurs et pour les programmeurs. Voici un exemple classique:

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

Ici, j'ai marqué les «mots clés de la langue» avec K et les identifiants (noms de variables) I. Étant donné qu'il n'y a pas de différence d'orthographe, je pense que vous pouvez probablement comprendre à quel point cela peut être déroutant. Bien sûr, ceci est un exemple extrême, et il est peu probable que quelqu'un ait jamais écrit du code comme ça exprès. Parfois , les gens ont « recycler » langue des mots clés comme noms d'identification si - et dans beaucoup de cas , une simple faute de frappe pourrait donner lieu à un code que la spécification de langage dit doit être analysé de cette façon, même si on n'a pas voulu du tout. Pour un autre exemple bien connu, comparez ceci:

do 10 i = 1,10

pour ça:

do 10 i = 1.10

Le premier est une boucle do - itérant 10 fois un bloc de code. Le second, cependant, a changé la virgule en virgule décimale, il s'agit donc d'affecter la valeur 1.10à une variable nommée do 10 i.

Cela signifiait également que l'écriture d'un analyseur Fortran était relativement difficile - vous ne pouviez pas être sûr que le dodébut de la ligne était vraiment un mot clé jusqu'à ce que vous atteigniez la fin de la ligne, et vérifié que tous les autres éléments d'un doboucle étaient présents. L'analyseur devait généralement être prêt à "revenir en arrière", en ré-analysant la ligne depuis le début pour arriver à la réponse "correcte" (mais souvent involontaire) de ce qui était vraiment là.

Après quelques années, les concepteurs de langage (la plupart d'entre eux de toute façon) sont allés vers l'extrême opposé - restreignant presque tout sur la langue autant que possible sans que les utilisateurs se plaignent trop .

Les premiers BASIC, par exemple, disaient essentiellement que vous ne pouviez même pas utiliser un mot clé dans le cadre d'un identifiant - par exemple, fora=1serait analysé comme for a = 1(c'est-à-dire le début d'une forboucle, pas une affectation). Cela a apparemment généré suffisamment de plaintes pour ne pas durer très longtemps. La règle concernant le démarrage d'un identifiant avec un chiffre n'a apparemment pas généré beaucoup de plaintes, donc il continue d'être utilisé (au moins dans la plupart des langues).

Jerry Coffin
la source
À mon humble avis, c'est plus proche de la vraie raison. Les premiers langages tels que Fortran étaient, à certains égards, trop peu structurés, ce qui a entraîné des difficultés à écrire des compilateurs robustes et des difficultés pour les humains à analyser visuellement correctement le code source. Le "do10i = ..." est un exemple classique et célèbre. À mesure que les langues évoluaient, certaines règles ont été resserrées. Algol est probablement le grand-père de la règle générale «les identificateurs commencent par des lettres et peuvent ensuite avoir des lettres ou des chiffres».
Larry Gritz
FYI, l'interpréteur Microsoft BASIC qui a constitué la base des versions de micro-ordinateur les plus populaires de BASIC (y compris Applesoft Basic et Commodore Basic) a utilisé un tokenizer gourmand pour convertir toute séquence de caractères correspondant à un jeton de langue en une valeur d'octet avec l'ensemble de bits élevé. Cela a été fait sans aucune analyse syntaxique. Ensuite, lors de l'exécution du programme, l'interpréteur supposerait que toutes les lettres trouvées faisaient partie d'un nom de variable.
supercat
1

Cette convention a probablement évolué à partir des premières décisions de conception du langage historique, car sur les premières machines, le compilateur entier, y compris l'analyse lexicale, devait fonctionner sur quelques kWords, moins de mémoire que même le cache de données du processeur de premier niveau sur les appareils mobiles actuels, les noms de variables autorisés étaient donc très limités et devaient être faciles à distinguer des constantes numériques dans très peu de codes op.

Ainsi, la convention est devenue ce à quoi les générations de programmeurs sont habituées.

hotpaw2
la source
1

Ce n'est pas une règle logiquement requise pour le langage de programmation mais juste la convention utilisée par de nombreux concepteurs de langage.

Je peux concevoir un langage radicalement différent qui autorise tous les caractères pour les identifiants. Pour toutes les lignes de code, les 20 premiers caractères décrivent le type d'instruction, puis les 20 caractères suivants définissent le premier symbole de l'instruction et les 20 caractères suivants sont l'opérande de l'instruction. Ce langage sera exécuté sur un processeur de pile.

01234567890123456789 01234567890123456789 01234567890123456789

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

Ce code pourrait être traduit en C comme ci-dessous:

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

C'est tout. Cela n'a pas de sens et la règle de non-nombre dans les identificateurs est également inutile sur le plan logique.

9dan
la source
0

En plus de "la commodité pour le lexer", je pense qu'il vaut également la peine de considérer la "commodité pour le lecteur".

Lors de la lecture du code, vous devez identifier rapidement et à plusieurs reprises quels mots sont des identifiants et quels sont des nombres. La recherche d'un chiffre au début est plus facile avec notre correspondance visuelle des motifs; ce serait une corvée si nous devions vérifier soigneusement tous les personnages pour nous en assurer.

tempête à venir
la source
0

La réponse à cette question réside dans les automates ou plus précisément les automates finis qui définissent l'expression régulière. La règle est ... les compilateurs ont besoin d'algorithmes ou de règles exacts pour décider de chaque caractère qu'ils analysent. Si les identifiants étaient autorisés à commencer par un nombre, alors le compilateur sera dans un correctif..à propos de la nature du jeton à venir ... sera-ce un nombre ou un identifiant ... et comme les compilateurs ne peuvent pas revenir en arrière vers des positions antérieures .. .so..pour faire comprendre au compilateur que le jeton à venir est précisément un identifiant ou un nombre ... cette restriction est là ... car ce compilateur ... sait simplement en scannant le premier caractère que le jeton à venir est un identifiant ou un nombre.

Waquas
la source