Perché gli identificatori non dovrebbero iniziare con un numero?


32

La maggior parte dei linguaggi di programmazione sembra essere progettata per non consentire a uno di dichiarare un identificatore che inizia con un numero. Ero solo curioso di sapere il motivo. Ho già cercato sul web, ma non sono riuscito a trovare una spiegazione soddisfacente.


4
Hai un singolo esempio di un nome di variabile in cui avrebbe un vantaggio per chiarezza e leggibilità?
Sicuro il

5
@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...
utente sconosciuto

6
Forth lo consente. Dei built-in: 2DUP, 2DROP, 2SWAP, 2> R, 2R @, 2R>, 0 =, ecc.
Peter Mortensen,

come fa TCL ma non credo che nessuno dei comandi TCL standard inizi con un numero
jk.

Risposte:


51

In C / C ++, un numero seguito da una lettera è considerato una costante numerica e la stringa che segue, qualifica il tipo di costante. Ad esempio (questi sono VC ++, non sono sicuro di quanto siano standard):

  • 0 - intero con segno
  • 0l - intero lungo firmato
  • 0u - numero intero senza segno
  • 0i64 - Numero intero con segno a 64 bit

Quindi a) è più facile per il lexer come ha detto Daniel ma anche b) fa una distinzione esplicita poiché 0y potrebbe essere una variabile ma 0u non lo sarebbe mai. Inoltre altri qualificatori, come "i64" sono stati aggiunti molto più tardi di "l" o "u" e vogliono mantenere aperta l'opzione di aggiungere altro se necessario.


7
inoltre, i numeri esadecimali sono scritti nella forma 0xd + dove d + è 1 più cifre esadecimali 0-f - quindi 0xbeef è un "numero" perfettamente valido.
Tcrosley,

20
voi ragazzi vi rendete conto che non stavo andando per una specifica linguistica, ma ho fornito solo alcuni esempi per illustrare il punto, giusto?
DXM,

6
Ri: "vogliono mantenere aperta l'opzione di aggiungere altro se necessario": E C ++ 11 ti consente anche di aggiungere il tuo; vedi http://en.wikipedia.org/wiki/C++11#User-defined_literals .
Ruakh,

2
Non penso che questa sia la spiegazione giusta. La regola "identificatore non può iniziare con una cifra" era vera per Algol, Pascal e altre lingue che non consentivano i suffissi alfabetici alle costanti numeriche.
Larry Gritz,

1
@LarryGritz: "La separazione coerente delle parole dagli spazi divenne un'usanza generale intorno al X secolo d.C. e durò fino al 1957 circa, quando FORTRAN abbandonò la pratica". —Sun FORTRAN Reference Manual (dal wiki). Fortran aveva le sue ragioni speciali perché avevano deciso che gli spazi in generale erano opzionali. Le lingue moderne come il loro spazio bianco. Sei da solo con Algol ma non sono neanche moderno. D'altra parte C / C ++ / C # / F # hanno tutti i suffissi.
DXM,

49

La comodità delle persone che implementano il lexer. (No, sul serio, questo è tutto. Varie lingue hanno altri motivi, ma alla fine si riduce a quello.)


2
Sarebbe facile distinguere tra letterali integrali e identificatori che iniziano con cifre usando PEG o altre moderne tecniche di analisi. Anche i compilatori che usano i lexer primitivi potrebbero metterli nella stessa categoria di token e differenziarli in seguito. Sarebbe molto imbarazzante se, ad esempio, 0flufosse un letterale e 0gluun identificatore locale.
Daniel Lubarov,

2
È assolutamente possibile per le persone distinguerli. La decisione viene presa in base alla convenienza (o, se si è meno caritatevoli, alla pigrizia) piuttosto che ai requisiti tecnici.
Daniel Pittman,

2
@DanielPittman: Avresti bisogno di analisi semantiche per fare qualsiasi sorta di affidabile chiarimento delle ambiguità, quindi questo non può essere fatto nel lexer. L'espulsione della decisione dal lexer rende il parser più complesso e con quali vantaggi? Oltre alla pessima situazione costi / benefici, non esiste un buon modo per gestire un caso come int 0u = 5; unsigned int x = 0u;Tuttavia si sceglie di definire l'interpretazione di questo codice (probabilmente x == 0 o x == 5), le persone saranno confuse a causa dell'ambiguità. Anche se fosse banale implementare il compilatore in questo modo, un buon designer probabilmente non lo farebbe.
Joren,

10
La comodità principale è per il parser nella mia testa e non per il creatore della lingua.
CodesInChaos,

2
È ancora una sorpresa per molte persone apprendere che l'analisi lessicale è di solito un fattore determinante nella fase più lenta di un compilatore / interprete.
hippietrail,

20

Considera i seguenti 2 casi:

Caso 1

Supponiamo che un identificatore possa iniziare con un numero.

Quindi un'istruzione come di seguito sarebbe valida (poiché un identificatore può avere 1 o più caratteri):

int 3;

Quando provo a utilizzare la variabile sopra in un programma, si ottiene l'ambiguità del compilatore:

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

Nella dichiarazione a=3qual è il ruolo di 3 (è una variabile con valore 5 o è il numero 3)?

Caso 2

Contrariamente all'esempio precedente, supponiamo che una lingua consenta effettivamente agli identificatori di iniziare con un numero, pur non consentendone l'utilizzo come identificativi. Ciò può causare i seguenti problemi:

  • Le regole del linguaggio relative alla variabile che dice che una variabile può essere composta da 1 o più caratteri dovranno essere ridefinite in una regola complessa come: Una variabile può avere uno o più caratteri e deve essere unica se non inizia con un numero mentre non può avere una lunghezza di un singolo carattere quando inizia con un numero (ecc.)

  • Il compilatore dovrà verificare e segnalare casi di errore quando tutti i numeri (ad es. 333) e i suffissi alfabetici validi (ad es. 34L) vengono utilizzati come nomi di variabili. In linguaggi tipicamente vaghi come Python e JS in cui è possibile utilizzare le variabili al volo senza dichiararle, potrebbe anche essere impossibile verificare i casi speciali che coinvolgono tutti i numeri, ad esempio if (33==5)Qui, 33 potrebbe essere una variabile non dichiarata errata dichiarata dall'utente. Ma il compilatore non sarà in grado di identificarlo e segnalare l'errore.

Fare questa restrizione impedirà al programmatore di usare numeri come nomi identificativi.


2
In base a questa logica, gli identificatori non potevano contenere caratteri poiché sarebbero ambigui rispetto alle parole chiave. Riesci a immaginare quanto int char = floatsarebbe disastroso ?
Pubblicazione

4
@Pubby: non vedo come puoi estrapolare ciò che ho detto ad un totale non senso che non riesco ancora a capire. Cosa significa il tuo commento?
aml90,

Sto dicendo che stai prendendo la domanda troppo alla lettera e che non è affatto ambiguo usando la precedenza lessicale. Ad esempio, come fa il compilatore a sapere che intè una parola chiave e non un identificatore? Bene, intha una precedenza maggiore proprio come i lessemi numerici.
Pubblicazione

@Pubby: Per ambiguità intendevo dire che il compilatore non avrebbe saputo in quale contesto sto usando il nome della variabile (anche usando la precedenza lessicale). Ad esempio, considera questo codice: int 3,a; 3=5; a=3; nell'istruzione a = 3, 3 viene interpretato come identificatore o come numero? Questo provoca ambiguità. Spero sia chiaro.
aml90,

2
Trovo anche questo argomento debole. Sarebbe banale scrivere un lexer che accetterebbe identificatori che iniziano con, ma non sono interamente composti da numeri.
Larry Gritz,

11

Nella maggior parte dei casi ciò non ha nulla a che fare con la semplificazione degli autori del compilatore e l'analisi dell'efficienza, ma soprattutto con la progettazione di una sintassi che incoraggi un codice chiaro e inequivocabile.

I designer del linguaggio hanno pensato che sarebbe stato bello poter scrivere letterali numerici come il numero 1 come solo 1 .

Sarebbe del tutto possibile progettare una sintassi linguistica in cui i letterali numerici fossero citati in qualche modo, ad esempio tildas, quindi il letterale numerico per il numero uno era codificato come ~ 1 ~ e tutto ciò che non era una parola chiave e non racchiuso tra virgolette veniva trattato come un nome di variabile .

Quindi potresti codificare istruzioni come:

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

Ma anche:

2 = ~3~
six = 2 + 2

Qualunque sia la sintassi scelta, il codice ambiguo e difficile da seguire è inevitabile.

Il linguaggio C e la maggior parte delle lingue "parentesi graffe" discendenti da C hanno anche ritenuto una buona idea consentire ai programmatori di codificare direttamente i letterali ottali ed esadecimali e, se questo era importante, specificare il tipo di letterale. Così

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

Quindi, anche se consentissi che i nomi delle variabili inizino con un numero seguito da una combinazione di numeri e lettere che includessero almeno una lettera, presenteresti al programmatore il problema di decidere se un determinato gruppo formasse un nome di variabile o un valore letterale numerico, quindi

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

Tale ambiguità non aiuterebbe nessuno a scrivere o leggere un programma.

Per un esempio del mondo reale strettamente correlato, puoi guardare il linguaggio PL / 1 i cui progettisti hanno pensato che poter usare le parole chiave come nomi di variabili fosse una buona idea in modo che:

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

Codice valido che compila ed esegue.


C è stato progettato come assemblaggio portatile per Unix. Unix è stato originariamente progettato per una macchina a 18 bit, dove ottale è adatto per la stampa nello stesso modo in cui hex è adatto per la stampa di valori di macchina a 8/16/32 bit. Quindi avevano effettivamente bisogno di ottale.

Anche per i bit twiddling (OR, XOR, AND, NOT) e l'implementazione dei driver di dispositivo è importante specificare la dimensione esatta di un valore letterale e il valore!
James Anderson,

10

Fortran ebbe un enorme effetto su come furono progettate le lingue successive. All'inizio (alcuni di questi problemi sono stati risolti) Fortran non aveva quasi alcuna regola che limitasse il nome che si poteva dare a un identificatore. Ciò ha reso il linguaggio estremamente difficile da analizzare sia per i compilatori che per i programmatori. Ecco un classico esempio:

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

Qui ho contrassegnato le "parole chiave della lingua" con K e gli identificatori (nomi delle variabili) I. Dato che non c'è alcuna differenza nell'ortografia, penso che tu possa probabilmente capire quanto possa essere confuso. Naturalmente, questo è un esempio estremo, ed è improbabile che qualcuno abbia mai scritto un codice del genere apposta. A volte le persone hanno fatto "riciclare" le parole chiave della lingua come identificatori però - e in molti casi un semplice errore di battitura potrebbe provocare codice che la specifica lingua detto dovrebbe essere analizzato in questo modo, anche se non era destinato a tutti. Per un altro esempio ben noto, confronta questo:

do 10 i = 1,10

a questa:

do 10 i = 1.10

Il primo è un ciclo do - iterando un blocco di codice 10 volte. Il secondo, tuttavia, ha avuto la virgola cambiata in un punto decimale, quindi sta assegnando il valore 1.10a una variabile denominata do 10 i.

Ciò significava anche che scrivere un parser Fortran era relativamente difficile: non si poteva essere certi che doall'inizio della riga fosse davvero una parola chiave fino a quando non si raggiungesse la fine della riga e si verificasse che tutti gli altri elementi di un doerano presenti loop. Il parser doveva in genere essere pronto a "tornare indietro", analizzando nuovamente la linea dall'inizio per arrivare alla risposta "corretta" (ma spesso non intenzionale) di ciò che era realmente lì.

Dopo alcuni anni, i progettisti del linguaggio (la maggior parte comunque) sono andati all'estremo opposto, limitando il più possibile quasi tutto il linguaggio senza che gli utenti si lamentino troppo .

All'inizio BASIC, ad esempio, sostanzialmente diceva che non potevi nemmeno usare una parola chiave come parte di un identificatore - per esempio, fora=1verrebbe analizzato come for a = 1(cioè l'inizio di un forciclo, non di un compito). Ciò apparentemente ha generato abbastanza lamentele da non durare a lungo. La regola sull'avvio di un identificatore con una cifra apparentemente non ha generato molti reclami, quindi continua ad essere utilizzata (almeno nella maggior parte delle lingue).


IMHO questo è il più vicino alla vera ragione. I primi linguaggi come Fortran erano, in un certo senso, troppo destrutturati, con difficoltà a scrivere compilatori robusti e difficoltà per gli umani a analizzare visivamente correttamente il codice sorgente. "Do10i = ..." è un esempio classico e famoso. Con l'evolversi delle lingue, alcune regole sono state rafforzate. Algol è probabilmente il nonno dello standard "gli identificatori iniziano con le lettere e in seguito possono avere lettere o numeri" regola empirica.
Larry Gritz,

Cordiali saluti, l'interprete Microsoft BASIC che ha costituito la base delle versioni più popolari di Microcomputer di BASIC (inclusi Applesoft Basic e Commodore Basic) ha utilizzato un tokenizer avido per convertire qualsiasi sequenza di caratteri che corrispondeva a un token di lingua in un valore byte con il bit alto impostato. Ciò è stato fatto senza alcuna analisi sintattica. Quindi, durante l'esecuzione del programma, l'interprete presupponeva che qualsiasi lettera trovata costituisse parte di un nome di variabile.
supercat

1

Probabilmente questa convenzione si è evoluta da decisioni di progettazione del linguaggio storico molto precoci, poiché sulle prime macchine l'intero compilatore, compresa l'analisi lessicale, doveva funzionare in pochi kWord, meno memoria rispetto alla cache dei dati del processore di primo livello sui dispositivi mobili attuali, quindi i nomi delle variabili consentite erano molto limitati e dovevano essere facili da distinguere dalle costanti numeriche in pochissimi codici op.

Pertanto, la convenzione è diventata ciò a cui sono abituate generazioni di programmatori.


1

Non è una regola logicamente richiesta per il linguaggio di programmazione, ma solo la convenzione utilizzata da molti progettisti di linguaggi.

Sono in grado di progettare un linguaggio radicalmente diverso che consenta a tutti i caratteri di identificare. Per tutte le righe di codice, i primi 20 caratteri descriveranno il tipo di istruzione, quindi i successivi 20 caratteri definiranno il primo simbolo per l'istruzione e i successivi 20 caratteri saranno operando per l'istruzione. Questa lingua verrà eseguita su un processore stack.

01234567890123456789 01234567890123456789 01234567890123456789

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

Questo codice può essere tradotto in C come di seguito:

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

È tutto. È insignificante e la regola del non numero in identificatori è anche inutile in termini logici.


0

Oltre a "convenienza per il lexer", penso che valga la pena considerare anche "praticità per il lettore".

Durante la lettura del codice, è necessario identificare rapidamente e ripetutamente quali parole sono identificatori e quali sono numeri. Cercare una cifra all'inizio è più facile sulla nostra corrispondenza visiva del modello; sarebbe un lavoro ingrato se dovessimo controllare attentamente tutti i personaggi per essere sicuri.


0

La risposta a questa domanda sta negli automi o più precisamente automi finiti che definiscono l'espressione regolare. La regola è ... i compilatori hanno bisogno di algoritmi o regole esatti per decidere su ogni personaggio che analizzano. Se gli identificatori fossero autorizzati a iniziare con un numero, il compilatore sarà in una correzione ... circa la natura del token in arrivo ... sarà un numero o un identificatore ... e come compilatori non possono tornare indietro alle posizioni precedenti ... .so..per chiarire al compilatore che il token in arrivo è precisamente un identificatore o un numero ... questa limitazione è lì ... perché questo compilatore sa solo scansionando il primo carattere che il token in arrivo è un identificatore o un numero.

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.