Perché i nomi delle variabili non possono iniziare con i numeri?


136

Stavo lavorando con un nuovo sviluppatore C ++ qualche tempo fa quando mi ha posto la domanda: "Perché i nomi delle variabili non possono iniziare con i numeri?"

Non sono riuscito a trovare una risposta, tranne per il fatto che alcuni numeri possono contenere del testo (123456L, 123456U) e ciò non sarebbe possibile se i compilatori pensassero che tutto con una certa quantità di caratteri alfa fosse un nome variabile.

Era la risposta giusta? Ci sono altri motivi?

string 2BeOrNot2Be = "that is the question"; // Why won't this compile?

15
E perché non possono avere spazi in loro?
Tim

4
Questo problema precede il C ++ di almeno 20 anni, se non di nuovo ai primi macroassemblatori.
Ken Gentle,

2
Bene, in FORTH, puoi farlo. AFAIK, c'è una parola chiamata 0che spinge 0 nello stack. un altro è 0=che controlla se 0 è nello stack.
Ingo,

12
Perché questa domanda è così popolare e le risposte così sbagliate? Molte lingue consentono alle variabili di iniziare con i numeri. Il C ++ non lo fa, ma è solo una comoda limitazione che evita determinate ambiguità. A volte SO mi stupisce in tutti i modi sbagliati.
david.pfx,

5
Se questa domanda è stata posta oggi su SO, sarà definita basata sull'opinione e chiusa. Grazie per averlo chiesto.
Boon,

Risposte:


116

Perché allora una stringa di cifre sarebbe un identificatore valido e un numero valido.

int 17 = 497;
int 42 = 6 * 9;
String 1111 = "Totally text";

37
Bene, e se dicessero che le variabili non possono essere solo numeri. E allora?
Pirolistico il

6
Mi ci vorrebbe più tempo a trovare un'espressione regolare affinché il lexer raccolga gli identificativi usando quella regola, se è possibile, quindi posso vedere perché nessun linguaggio è mai stato implementato in quel modo, oltre ai motivi indicati in altre risposte.
skiphoppy,

39
Se dovesse trattarsi di numeri + alfa, allora potresti comunque fare String 0x123 = "Hello World". A meno che non si affermi che i nomi delle variabili sono "numeri + alfa che non analizzano una designazione numerica valida", e questo è solo sciocco.
Eolson,

4
Non importa il compilatore: le persone che usano la lingua devono essere in grado di distinguere facilmente (a colpo d'occhio) i nomi delle variabili dai numeri. Se il primo personaggio non te lo dicesse - invece, se avessi bisogno di cercare nel resto della parola per dire se c'era un alfa non numerico da qualche parte lì dentro - il codice sarebbe più difficile da leggere.
comingstorm

10
@eaolson: ho lavorato con un assemblatore che ha applicato quella regola a numeri esadecimali che sono iniziati con A- Fe si sono conclusi con h. Mi ha fatto scattare la prima volta che ho provato a definire un'etichetta per indicare i dati musicali di Two Part Invention # 13 di Bach (nome logico? Bach).
supercat

116

Bene, pensa a questo:

int 2d = 42;
double a = 2d;

Che cos'è un? 2.0? o 42?

Suggerimento, se non lo capisci, d dopo un numero indica il numero prima che sia un doppio letterale


11
Questa è in realtà una notazione [relativamente] in arrivo ("d" per "doppio"), standard IIRC C89. I valori numerici iniziali negli identificatori non sono possibili se questo costrutto è nella lingua, ma non è questo il motivo per cui i numeri non possono avviare un identificatore.
Ken Gentle,

1
dnon è un suffisso letterale mobile valido in C ++. I letterali fluttuanti sono i doppi per impostazione predefinita, puoi usare fo lse hai bisogno di un float o di un letterale doppio lungo.
CB Bailey,

1
È per Java, e mentre la domanda originale era per C ++, si applica anche a molti altri linguaggi, come Java. Ma sono d'accordo. Questo non è il motivo originale per cui gli identificatori non possono iniziare con i numeri.
Pirolistico

50

Ora è una convenzione, ma è nata come requisito tecnico.

Ai vecchi tempi, i parser di lingue come FORTRAN o BASIC non richiedevano l'uso degli spazi. Quindi, sostanzialmente, i seguenti sono identici:

10 V1=100
20 PRINT V1

e

10V1=100
20PRINTV1

Supponiamo ora che siano consentiti prefissi numerici. Come lo interpreteresti?

101V=100

come

10 1V = 100

o come

101 V = 100

o come

1 01V = 100

Quindi, questo è stato reso illegale.


1
Minor nit: i numeri di riga dovevano essere nelle colonne 1-6 e il codice eseguibile che segue la colonna 8. D'altra parte DO 10 I=1,50potrebbe essere analizzato ambiguamente come DO1 0I=1,50[per inciso, se si usa un punto anziché una virgola, l'istruzione diventa un'assegnazione a un variabile a virgola mobile denominata DO10I.
supercat

Spiegazione interessante! Questo ha senso per le lingue più vecchie, mi chiedo ancora perché abbiamo ancora continuato la scelta del design per lingue come Python o JavaScript o R.
Charles Clayton,

Lo ricordo sicuramente con BASIC e penso che questo sia probabilmente il motivo pratico più valido della pratica. Tecnicamente però, ricordo vagamente che potrebbe effettivamente tornare al linguaggio dell'assemblea precoce. Non sono sicuro di ciò che l'assemblatore però, e molto bene potrei sbagliarmi.
Brian Chandler,

42

Perché il backtracking è evitato nell'analisi lessicale durante la compilazione. Una variabile come:

Apple;

il compilatore saprà subito che è un identificatore quando incontra la lettera "A".

Tuttavia una variabile come:

123apple;

il compilatore non sarà in grado di decidere se si tratta di un numero o identificatore fino a quando non raggiunge "a" e, di conseguenza, deve tornare indietro.


2
Per rispondere ricordando la mia classe di progetti di compilatore, questa risposta va dritta! Kudos
nehem

15

Compilatori / parser / analizzatori lessicali sono stati molto, molto tempo fa per me, ma penso di ricordare che ci sono difficoltà nel determinare in modo inequivocabile se un carattere numerico nell'unità di compilazione rappresentasse un letterale o un identificatore.

Le lingue in cui lo spazio è insignificante (come ALGOL e l'originale FORTRAN se ricordo bene) non potevano accettare numeri per iniziare identificatori per quel motivo.

Questo risale a prima - prima delle notazioni speciali per indicare la memoria o la base numerica.


9

Sono d'accordo che sarebbe utile consentire agli identificatori di iniziare con una cifra. Una o due persone hanno detto che puoi aggirare questa limitazione anteponendo un carattere di sottolineatura al tuo identificatore, ma è davvero brutto.

Penso che parte del problema derivi da numeri letterali come 0xdeadbeef, che rendono difficile elaborare regole facili da ricordare per gli identificatori che possono iniziare con una cifra. Un modo per farlo potrebbe essere quello di consentire qualsiasi cosa corrispondente [A-Za-z _] + che NON sia una parola chiave o un numero letterale. Il problema è che porterebbe a cose strane come 0xdeadpork, ma non 0xdeadbeef. In definitiva, penso che dovremmo essere onesti con tutte le carni: P.

Quando ho iniziato a studiare C, ricordo di aver sentito che le regole per i nomi delle variabili erano arbitrarie e restrittive. Peggio ancora, erano difficili da ricordare, quindi ho rinunciato a cercare di impararli. Ho appena fatto ciò che mi sembrava giusto e ha funzionato abbastanza bene. Ora che ho imparato molto di più, non sembra poi così male e alla fine sono riuscito a impararlo nel modo giusto.


8
LOL - "Il problema è che porterebbe a cose strane come 0xdeadpork, ma non 0xdeadbeef. In definitiva, penso che dovremmo essere giusti con tutte le carni: P."
sig. Euro

6

È probabile che sia stata presa una decisione per alcuni motivi, quando stai analizzando il token devi solo guardare il primo carattere per determinare se si tratta di un identificatore o letterale e quindi inviarlo alla funzione corretta per l'elaborazione. Quindi questa è un'ottimizzazione delle prestazioni.

L'altra opzione sarebbe quella di verificare se non è un valore letterale e lasciare il dominio degli identificatori come l'universo meno i letterali. Ma per fare questo dovresti esaminare ogni personaggio di ogni token per sapere come classificarlo.

Ci sono anche implicazioni stilistiche che gli identificatori dovrebbero essere mnemonici, quindi le parole sono molto più facili da ricordare dei numeri. Quando molte delle lingue originali venivano scritte impostando gli stili per i prossimi decenni, non stavano pensando di sostituire "2" con "a".


6

I nomi delle variabili non possono iniziare con una cifra, perché possono causare alcuni problemi come di seguito:

int a = 2;
int 2 = 5;
int c = 2 * a; 

qual è il valore di c? è 4 o è 10!

un altro esempio:

float 5 = 25;
float b = 5.5;

è il primo 5 un numero o è un oggetto (. operator) C'è un problema simile con il secondo 5.

Forse, ci sono altri motivi. Quindi, non dovremmo usare nessuna cifra all'inizio di un nome di variabile.


Anche se si richiedesse che gli identificatori contengano almeno un carattere non numerico, si dovrebbe anche richiedere che i formati numerici che contengono lettere debbano contenere anche un carattere non alfanumerico [ad es. Richiedere 0x1234 per essere scritto come $ 1234 e 1E6 per essere scritto come 1.E6 o 1.0E6] oppure hanno una strana combinazione di nomi identificativi legali e illegali.
supercat,

4

L'uso di una cifra per iniziare un nome di variabile rende molto più complicato il controllo degli errori durante la compilazione o l'interpretazione.

Consentire l'uso di nomi di variabili iniziati come un numero probabilmente causerebbe enormi problemi ai progettisti del linguaggio. Durante l'analisi del codice sorgente, ogni volta che un compilatore / interprete incontrava un token che iniziava con una cifra in cui era previsto un nome di variabile, avrebbe dovuto cercare un insieme enorme e complicato di regole per determinare se il token era davvero una variabile o un errore . La complessità aggiunta al parser di lingua potrebbe non giustificare questa funzionalità.

Per quanto io possa ricordare (circa 40 anni), non penso di aver mai usato una lingua che permettesse l'uso di una cifra per iniziare i nomi delle variabili. Sono sicuro che questo è stato fatto almeno una volta. Forse qualcuno qui l'ha visto da qualche parte.


1
Non è così difficile. Rende la fase lessicale più difficile, tutto qui. Naturalmente, quando ho preso i compilatori, mi è stato detto che la scansione lessicale poteva richiedere più di un quarto del tempo totale di compilazione.
David Thornley,

4

Come diverse persone hanno notato, c'è un sacco di bagaglio storico sui formati validi per i nomi delle variabili. E i progettisti di lingue sono sempre influenzati da ciò che sanno quando creano nuove lingue.

Detto questo, praticamente tutte le volte che una lingua non consente ai nomi di variabili di iniziare con i numeri è perché queste sono le regole del design della lingua. Spesso è perché una regola così semplice rende molto più semplice l'analisi e il lessing della lingua. Tuttavia, non tutti i progettisti di lingue sanno che questa è la vera ragione. I moderni strumenti di lexing aiutano, perché se hai provato a definirlo come ammissibile, ti daranno dei conflitti di analisi.

OTOH, se la tua lingua ha un carattere identificabile in modo univoco per annunciare i nomi delle variabili, è possibile impostarlo in modo che inizi con un numero. Variazioni di regole simili possono anche essere utilizzate per consentire spazi nei nomi delle variabili. Ma è probabile che la lingua risultante non assomigli molto a nessuna lingua convenzionale popolare, se non del tutto.

Per un esempio di un linguaggio di template HTML abbastanza semplice che permetta alle variabili di iniziare con numeri e avere spazi incorporati, guarda Qompose .


1
In realtà, ci sono diverse lingue che ti consentono di avere caratteri che identificano gli identificatori. Si chiamano "sigilli" e li hai in Perl e PHP.
Jason Baker,

Tranne che non ti è ancora permesso iniziare un nome di variabile in PHP con un numero - le regole del linguaggio lo vietano. :-) Ma puoi in Qompose esattamente per lo stesso motivo.
staticsan

4

Perché se permetti alla parola chiave e all'identificatore di iniziare con caratteri numerici, il lexer (parte del compilatore) non potrebbe facilmente distinguere tra l'inizio di un letterale numerico e una parola chiave senza diventare molto più complicato (e più lento).


2
Il processo di lexing è raramente il collo di bottiglia. Certo, rende la regex per i token identificativi più complessa, ma possono comunque essere DFA superveloci. Il tempo di esecuzione di questi è arachidi rispetto alla maggior parte delle altre attività che i compilatori devono svolgere.

4

La restrizione è arbitraria. Vari Lisps consentono ai nomi dei simboli di iniziare con numeri.



2

Il C ++ non può averlo perché i progettisti del linguaggio lo hanno reso una regola. Se dovessi creare la tua lingua, potresti certamente permetterlo, ma probabilmente incontreresti gli stessi problemi che hanno fatto e deciderai di non permetterlo. Esempi di nomi di variabili che potrebbero causare problemi:

0x, 2d, 5555


Questa limitazione è valida nelle lingue in cui non è consentito quel tipo di sintassi.
Jason Baker,

2

Uno dei problemi chiave riguardanti il ​​rilassamento delle convenzioni sintattiche è che introduce dissonanza cognitiva nel processo di codifica. Il modo in cui pensi al tuo codice potrebbe essere profondamente influenzato dalla mancanza di chiarezza che ciò introdurrebbe.

Non è stato Dykstra a dire che "l'aspetto più importante di qualsiasi strumento è il suo effetto sul suo utente"?


1

Probabilmente perché rende più facile per l'essere umano capire se si tratta di un numero o di un identificatore e per tradizione. Avere identificatori che potrebbero iniziare con una cifra non complicherebbe molto le scansioni lessicali.

Non tutte le lingue hanno vietato gli identificatori che iniziano con una cifra. In Forth, potevano essere numeri e piccoli numeri interi venivano normalmente definiti come parole Forth (essenzialmente identificatori), poiché era più veloce leggere "2" come una routine per spingere un 2 nello stack piuttosto che riconoscere "2" come un numero il cui valore era 2. (Nell'elaborare l'input dal programmatore o dal blocco del disco, il sistema Forth suddivideva l'input in base agli spazi. Provava a cercare il token nel dizionario per vedere se era una parola definita, e in caso contrario tenterebbe di tradurlo in un numero e in caso contrario segnalerebbe un errore.)


Il fatto è che Forth non ha davvero un parser molto sofisticato. In realtà, tutto ciò che interessa è se un identificatore si trova tra due serie di spazi bianchi.
Jason Baker,

1

Supponiamo che tu abbia permesso ai nomi dei simboli di iniziare con i numeri. Supponiamo ora di voler nominare una variabile 12345foobar. Come lo differenzeresti da 12345? In realtà non è terribilmente difficile fare con un'espressione regolare. Il problema è in realtà quello delle prestazioni. Non riesco davvero a spiegare perché questo sia molto dettagliato, ma essenzialmente si riduce al fatto che la differenziazione di 12345foobar da 12345 richiede il backtracking. Questo rende l'espressione regolare non deterministica.

C'è una spiegazione molto migliore di questo qui .


1
Come si progetterebbe un'espressione regolare per consentire una variabile denominata ifqo doublezma non ifo double? Il problema fondamentale nel consentire agli identificatori di iniziare con le cifre sarebbe che esistono forme esistenti di letterali esadecimali e numeri in virgola mobile che consistono interamente di caratteri alfanumerici (le lingue userebbero qualcosa come $ 1234 o h'1234 invece di 0x1234 e richiedere numeri come 1E23 per includere un periodo, potrebbe evitare tale problema). Si noti che i tentativi di analisi regex di C possono già essere bloccati da cose come 0x12E+5.
supercat

1

è facile per un compilatore identificare una variabile usando ASCII sulla posizione della memoria piuttosto che il numero.


1

Il compilatore ha 7 fasi come segue:

  1. Analisi lessicale
  2. Analisi della sintassi
  3. Analisi semantica
  4. Generazione di codice intermedio
  5. Ottimizzazione del codice
  6. Generazione di codice
  7. Tabella dei simboli

Il backtracking viene evitato nella fase di analisi lessicale durante la compilazione del pezzo di codice. La variabile come Apple, il compilatore saprà subito che è un identificatore quando incontra il carattere lettera "A" nella fase di analisi lessicale. Tuttavia, una variabile come 123apple, il compilatore non sarà in grado di decidere se è un numero o identificatore fino a quando non colpisce "a" e deve tornare indietro per passare alla fase di analisi lessicale per identificare che si tratta di una variabile. Ma non è supportato nel compilatore.

Quando analizzi il token devi solo guardare il primo carattere per determinare se si tratta di un identificatore o letterale e quindi inviarlo alla funzione corretta per l'elaborazione. Quindi questa è un'ottimizzazione delle prestazioni.


0

Penso che la semplice risposta sia che può, la restrizione è basata sul linguaggio. In C ++ e molti altri non può perché la lingua non lo supporta. Non è integrato nelle regole per consentirlo.

La domanda è simile alla domanda: perché il re non può spostare quattro spazi alla volta negli scacchi? È perché negli scacchi questa è una mossa illegale. Può in un altro gioco sicuro. Dipende solo dalle regole che vengono giocate.


Solo che il C ++ è stato inventato di recente da persone che sono ancora vive. Possiamo chiedere loro perché hanno scelto le cose che hanno fatto e hanno respinto le alternative. Lo stesso non si applica agli scacchi.
Steve Jessop,

Ma non è questo il punto. È un'analogia sul perché non ci possono essere numeri all'inizio dei nomi delle variabili e la risposta più semplice è, perché le regole del linguaggio non lo consentono.
kemiller2002,

Certo, ma non credo che l'interrogante sia un imbecille. Probabilmente ha già lavorato tanto da solo. La domanda dell'IMO è "perché le regole della lingua non lo consentono?". Vuole colmare il divario tra la conoscenza delle regole e la loro comprensione.
Steve Jessop,

Sì, riflettendo su questo, ho capito dove stavi andando. Tu hai un punto. Immagino che stavo applicando un po 'il rasoio di Occam a un po' di libertà e supponevo che non ci fosse una vera risposta al perché tranne che le variabili non iniziano con i numeri, perché non ci sono numeri.
kemiller2002,

Non sto dicendo che ti sbagli, mente, a volte le decisioni degli organismi di standard C ++ superano la comprensione mortale, e finisci con "perché hanno dovuto decidere qualcosa e hanno deciso questo". Ma c'è almeno una domanda da porre :-)
Steve Jessop il

0

Inizialmente era semplicemente perché è più facile ricordare (si può dare più significato) nomi di variabili come stringhe anziché numeri sebbene i numeri possano essere inclusi nella stringa per migliorare il significato della stringa o consentire l'uso dello stesso nome di variabile ma hanno designato come avere un significato o un contesto separato, ma vicino. Ad esempio loop1, loop2 etc ti farebbero sempre sapere che eri in un loop e / o loop 2 era un loop all'interno di loop1. Quale preferiresti (ha più significato) come variabile: indirizzo o 1121298? Quale è più facile da ricordare? Tuttavia, se la lingua utilizza qualcosa per indicare che non è solo testo o numeri (come l'indirizzo $ in $), in realtà non dovrebbe fare la differenza poiché ciò direbbe al compilatore che ciò che segue deve essere trattato come una variabile ( in questo caso).


0

La variabile può essere considerata come un valore anche durante il tempo di compilazione dal compilatore, quindi il valore può richiamare il valore più e più volte in modo ricorsivo


0

Il backtracking viene evitato nella fase di analisi lessicale durante la compilazione del pezzo di codice . La variabile come Apple; , il compilatore saprà subito che è un identificatore quando incontra il carattere lettera "A" nella fase di analisi lessicale. Tuttavia, una variabile come 123apple; , il compilatore non sarà in grado di decidere se è un numero o identificatore fino a quando non raggiunge "a" e deve tornare indietro per passare alla fase di analisi lessicale per identificare che è una variabile. Ma non è supportato nel compilatore.

Riferimento


0

Non potrebbe esserci nulla di sbagliato in questo quando si tratta di dichiarare variabile. Ma c'è qualche ambiguità quando si tenta di utilizzare quella variabile da qualche altra parte in questo modo:

let 1 = "Ciao mondo!" stampa (1) stampa (1)

print è un metodo generico che accetta tutti i tipi di variabili. quindi in quella situazione il compilatore non sa a quale (1) fa riferimento il programmatore: 1 di valore intero o 1 che memorizza un valore di stringa. forse meglio per il compilatore in questa situazione per consentire di definire qualcosa del genere, ma quando si tenta di utilizzare questa roba ambigua, portare un errore con capacità di correzione a come correggere l'errore e cancellare questa ambiguità.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.