Warum sollten Identifikatoren nicht mit einer Zahl beginnen?

32

Die meisten Programmiersprachen scheinen so konzipiert zu sein, dass man keine Kennung deklarieren kann, die mit einer Zahl beginnt. Ich war nur neugierig, den Grund zu kennen. Ich habe bereits im Internet gesucht, konnte jedoch keine zufriedenstellende Erklärung finden.

Sivasubramaniam Arunachalam
quelle
4
Haben Sie ein einziges Beispiel für einen Variablennamen, bei dem Klarheit und Lesbarkeit von Vorteil sind?
Sichern Sie sich den
5
@Secure: 3dspline, 4seasonPizza, 2pdfConverter, 8bitInt, ...
Benutzer unbekannt
6
Forth erlaubt es. Von den eingebauten: 2DUP, 2DROP, 2SWAP, 2> R, 2R @, 2R>, 0 = usw.
Peter Mortensen
Wie TCL, aber ich glaube, keiner der Standard-TCL-Befehle beginnt mit einer Zahl
jk.
1
Möglicherweise auch auf SO: stackoverflow.com/questions/342152/…
Ciro Santilli am

Antworten:

51

In C / C ++ wird eine Zahl gefolgt von einem Buchstaben als numerische Konstante betrachtet, und die folgende Zeichenfolge kennzeichnet den Typ der Konstante. Also zum Beispiel (das sind VC ++, nicht sicher, wie Standard sie sind):

  • 0 - Ganzzahl mit Vorzeichen
  • 0l - Lange Ganzzahl mit Vorzeichen
  • 0u - Ganzzahl ohne Vorzeichen
  • 0i64 - 64-Bit-Ganzzahl mit Vorzeichen

So a) es einfacher für den Lexer ist wie Daniel sagte aber auch b) es eine explizite Unterscheidung macht da 0y könnte eine Variable sein , aber 0HE würde nie sein. Außerdem wurden andere Qualifikationsmerkmale wie "i64" viel später als "l" oder "u" hinzugefügt, und sie möchten die Option offen halten, bei Bedarf weitere hinzuzufügen.

DXM
quelle
7
Außerdem werden Hexadezimalzahlen in der Form 0xd + geschrieben, wobei d + 1 weitere Hexadezimalziffern von 0 bis f sind, sodass 0xbeef eine vollkommen gültige "Zahl" ist.
Tcrosley
20
Ihr merkt, dass ich mich nicht für eine Sprachspezifikation entschieden habe, sondern nur ein paar Beispiele zur Veranschaulichung des Sachverhalts bereitgestellt habe, oder?
DXM
6
Betreff: "Sie möchten die Option offen halten, bei Bedarf weitere hinzuzufügen": In C ++ 11 können Sie sogar Ihre eigenen hinzufügen. siehe http://en.wikipedia.org/wiki/C++11#User-defined_literals .
Ruakh
2
Ich denke nicht, dass dies die richtige Erklärung ist. Die Regel "Bezeichner darf nicht mit einer Ziffer beginnen" galt für Algol, Pascal und andere Sprachen, die keine alphabetischen Suffixe für numerische Konstanten zuließen.
Larry Gritz
1
@ LarryGritz: "Das konsequente Trennen von Wörtern durch Leerzeichen wurde um das zehnte Jahrhundert zu einem allgemeinen Brauch und dauerte bis etwa 1957, als FORTRAN die Praxis aufgab." - Sun FORTRAN-Referenzhandbuch (aus dem Wiki). Fortran hatte seine eigenen besonderen Gründe, weil sie entschieden, dass Leerzeichen im Allgemeinen optional sind. MODERNE Sprachen mögen ihre Leerzeichen. Du bist alleine mit Algol, aber ich bin auch nicht so modern. Andererseits haben C / C ++ / C # / F # alle Suffixe.
DXM
49

Die Bequemlichkeit der Leute, die den Lexer implementieren. (Nein, im Ernst, das ist es. Verschiedene Sprachen haben andere Gründe, aber letztendlich kommt es darauf an.)

Daniel Pittman
quelle
2
Es wäre leicht, zwischen ganzzahligen Literalen und Bezeichnern zu unterscheiden, die mit Ziffern beginnen, die PEGs oder andere moderne Analysetechniken verwenden. Sogar Compiler, die primitive Lexer verwenden, könnten sie in dieselbe Token-Kategorie einordnen und später differenzieren. Es wäre nur sehr umständlich, wenn z. B. 0fluein Literal und 0glueine lokale Kennung wäre.
Daniel Lubarov
2
Es ist absolut möglich , für die Menschen , sie zu unterscheiden. Die Entscheidung wird eher aufgrund der Bequemlichkeit (oder, wenn Sie weniger gemeinnützig sind, Faulheit) als aufgrund der technischen Anforderungen getroffen.
Daniel Pittman
2
@DanielPittman: Sie benötigen eine semantische Analyse, um eine verlässliche Disambiguierung durchzuführen, sodass dies im Lexer nicht möglich ist. Wenn Sie die Entscheidung aus dem Lexer verdrängen, wird der Parser komplexer, und zu welchem ​​Vorteil? Abgesehen von der sehr schlechten Kosten-Nutzen-Situation gibt es einfach keine gute Möglichkeit, einen Fall wie den folgenden zu behandeln. int 0u = 5; unsigned int x = 0u;Wenn Sie jedoch die Interpretation dieses Codes definieren (wahrscheinlich entweder x == 0 oder x == 5), werden die Menschen verwirrt sein wegen der Mehrdeutigkeit. Selbst wenn es trivial wäre, den Compiler auf diese Weise zu implementieren, würde ein guter Designer dies wahrscheinlich nicht tun.
Joren
10
Der Hauptnutzen ist für den Parser in meinem Kopf und nicht für den Schöpfer der Sprache.
CodesInChaos
2
Viele Leute wundern sich immer noch darüber, dass die lexikalische Analyse in der Regel der langsamste Teil eines Compilers / Interpreters ist.
Hippietrail
20

Betrachten Sie die folgenden 2 Fälle:

Fall 1

Nehmen wir an, dass ein Bezeichner mit einer Zahl beginnen könnte.

Eine Aussage wie die folgende wäre also gültig (da ein Bezeichner 1 oder mehr Zeichen haben kann):

int 3;

Wenn ich versuche, die obige Variable in einem Programm zu verwenden, führt dies zu einer Mehrdeutigkeit des Compilers:

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

a=3Was ist die Rolle von 3 in der Anweisung (ist es eine Variable mit dem Wert 5 oder ist es die Zahl 3)?

Fall 2

Im Gegensatz zu dem obigen Beispiel nehmen wir an, dass eine Sprache tatsächlich Bezeichner zulässt, die mit einer Zahl beginnen, und weiterhin die Verwendung von Ziffern als Bezeichner nicht zulässt. Dies kann die folgenden Probleme verursachen:

  • Die Sprachregeln bezüglich der Variablen, die besagen, dass eine Variable aus einem oder mehreren Zeichen bestehen kann, müssen zu einer komplexen Regel umdefiniert werden: Eine Variable kann ein oder mehrere Zeichen haben und muss eindeutig sein, wenn sie nicht mit einer Zahl beginnt Es darf keine einzelne Zeichenlänge haben, wenn mit einer Zahl (usw.) begonnen wird.

  • Der Compiler muss nach Fehlern suchen und diese melden, wenn alle Ziffern (z. B. 333) und gültigen Buchstabensuffixe (z. B. 34L) als Variablennamen verwendet werden. In lose geschriebenen Sprachen wie Python und JS, in denen Sie Variablen im laufenden Betrieb verwenden können, ohne sie zu deklarieren, kann es sogar unmöglich sein, nach Sonderfällen zu suchen, die alle Ziffern betreffen if (33==5). Der Compiler kann dies jedoch nicht erkennen und den Fehler melden.

Durch diese Einschränkung wird der Programmierer daran gehindert, Zahlen als Bezeichnernamen zu verwenden.

aml90
quelle
2
Nach dieser Logik dürfen Bezeichner keine Zeichen enthalten, da sie für Schlüsselwörter nicht eindeutig sind. Können Sie sich vorstellen, wie katastrophal das int char = floatwäre?
Pubby
4
@Pubby: Ich verstehe nicht, wie man das, was ich gesagt habe, auf einen völlig unsinnigen Menschen hochrechnen kann, den ich noch nicht herausfinden kann. Was bedeutet dein Kommentar?
aml90
Ich sage, dass Sie die Frage zu wörtlich nehmen und dass es durch die Verwendung von Lexing-Vorrang überhaupt nicht mehrdeutig ist. Woher weiß der Compiler beispielsweise, dass es sich intum ein Schlüsselwort und nicht um eine Kennung handelt? Nun, inthat eine höhere Priorität, genau wie es nummerische Lexeme getan hätten.
Pubby
@Pubby: Mit Mehrdeutigkeit meinte ich, dass der Compiler nicht wissen würde, in welchem ​​Kontext ich den Variablennamen verwende (sogar mit lexikalischer Priorität). Betrachten Sie beispielsweise diesen Code: Wird int 3,a; 3=5; a=3; in der Anweisung a = 3 3 als Bezeichner oder als Zahl interpretiert? Dies führt zu Mehrdeutigkeiten. Hoffe es ist klar.
aml90
2
Ich finde dieses Argument auch schwach. Es wäre trivial, ein Lexer zu schreiben, das Bezeichner akzeptiert, die mit Ziffern beginnen, aber nicht vollständig aus diesen bestehen.
Larry Gritz
11

Dies hat größtenteils nichts damit zu tun, dass es den Compilern leichter fällt und die Effizienz beim Parsen steigt, sondern vielmehr damit, dass eine Syntax entwickelt wird, die klar lesbaren und eindeutigen Code fördert.

Die Sprachdesigner fanden es schön, numerische Literale wie die Nummer 1 als einfache 1 schreiben zu können .

Es wäre durchaus möglich, eine Sprachsyntax zu entwerfen, in der numerische Literale in Anführungszeichen gesetzt werden, z. B. Tildas, sodass das numerische Literal für Nummer eins als ~ 1 ~ codiert und alles, was nicht in Anführungszeichen steht, als Variablenname behandelt wird .

Sie könnten also Anweisungen wie die folgenden codieren:

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

Aber auch:

2 = ~3~
six = 2 + 2

Unabhängig davon, welche Syntax Sie für mehrdeutigen und schwer zu verfolgenden Code wählen, ist dies unvermeidlich.

Die C-Sprache und die meisten der von C abgeleiteten "geschweiften Klammern" hielten es auch für eine gute Idee, Programmierern die direkte Codierung von Oktal- und Hexadezimal-Literalen zu ermöglichen und den Typ des Literal anzugeben, wenn dies wichtig ist. So

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

Selbst wenn Sie also zulassen, dass Variablennamen mit einer Zahl beginnen, gefolgt von einer Kombination aus Zahlen und Buchstaben, die mindestens einen Buchstaben enthält, würden Sie dem Programmierer das Problem stellen, zu entscheiden, ob eine gegebene Gruppe einen Variablennamen oder ein numerisches Literal bildet

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

Eine solche Mehrdeutigkeit würde niemandem helfen, ein Programm zu schreiben oder zu lesen.

Ein nahe verwandtes Beispiel aus der Praxis finden Sie in PL / 1, dessen Designer die Verwendung von Schlüsselwörtern als Variablennamen für eine gute Idee hielten.

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

Ist gültiger Code, der kompiliert und ausgeführt wird.

James Anderson
quelle
C wurde als portable Assembly für Unix entwickelt. Unix wurde ursprünglich für eine 18-Bit-Maschine entwickelt, bei der Oktal genau so gut zum Drucken passt, wie Hexadezimal zum Drucken von 8/16/32-Bit-Maschinenwerten. Daher brauchten sie eigentlich Oktal.
Auch für Bit-Twiddling (OR, XOR, AND, NOT) und die Implementierung von Gerätetreibern ist es wichtig, die genaue Größe eines Literal sowie den Wert anzugeben!
James Anderson
10

Fortran hatte einen großen Einfluss darauf, wie spätere Sprachen entworfen wurden. Frühzeitig (einige dieser Probleme wurden inzwischen behoben) hatte Fortran fast keine Regeln, die den Namen eines Bezeichners einschränkten. Dies machte es für Compiler und Programmierer äußerst schwierig, die Sprache zu analysieren. Hier ist ein klassisches Beispiel:

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

Hier habe ich die "Sprachschlüsselwörter" mit K und den Bezeichnern (Variablennamen) I markiert. Da es keinen Unterschied in der Schreibweise gibt, können Sie wahrscheinlich verstehen, wie verwirrend dies sein könnte. Dies ist natürlich ein extremes Beispiel, und es ist unwahrscheinlich, dass jemals jemand absichtlich einen solchen Code geschrieben hat. Manchmal "recycelten" die Leute jedoch Sprachschlüsselwörter als Bezeichnernamen - und in vielen Fällen könnte ein einfacher Tippfehler dazu führen, dass der Code, der in der angegebenen Sprachspezifikation angegeben ist, auf diese Weise analysiert wird, obwohl dies überhaupt nicht beabsichtigt war. Vergleichen Sie dies für ein anderes bekanntes Beispiel:

do 10 i = 1,10

dazu:

do 10 i = 1.10

Die erste ist eine do-Schleife, die einen Codeblock zehnmal durchläuft. Bei der zweiten wurde das Komma jedoch in einen Dezimalpunkt geändert, sodass der Wert 1.10einer benannten Variablen zugewiesen wird do 10 i.

Dies bedeutete auch, dass das Schreiben eines Fortran-Parsers relativ schwierig war - Sie konnten nicht sicher sein, dass der doZeilenanfang wirklich ein Schlüsselwort war, bis Sie das Zeilenende erreicht hatten, und überprüften, ob alle anderen Elemente von a doSchleife waren vorhanden. Der Parser musste im Allgemeinen bereit sein, einen "Backtrack" durchzuführen und die Zeile von Anfang an neu zu analysieren, um zu der "richtigen" (aber häufig unbeabsichtigten) Antwort auf das zu gelangen, was wirklich vorhanden war.

Nach ein paar Jahren gingen die Sprachdesigner (die meisten sowieso) in die entgegengesetzte Richtung - sie schränkten fast alles an der Sprache so weit wie möglich ein, ohne dass sich die Benutzer zu sehr beschwerten .

Frühe BASIC-Versionen haben beispielsweise im Grunde gesagt, dass Sie nicht einmal ein Schlüsselwort als Teil eines Bezeichners verwenden können. Sie werden zum Beispiel fora=1analysiert als for a = 1(dh als Beginn einer forSchleife, nicht als Zuweisung). Das hat anscheinend genug Beschwerden hervorgerufen, dass es nicht sehr lange dauerte. Die Regel, eine Kennung mit einer Ziffer zu beginnen, hat anscheinend nicht viele Beschwerden hervorgerufen, weshalb sie weiterhin verwendet wird (zumindest in den meisten Sprachen).

Jerry Sarg
quelle
IMHO ist dies dem wahren Grund am nächsten. Frühe Sprachen wie Fortran waren in gewisser Weise zu unstrukturiert, was zu Schwierigkeiten beim Schreiben robuster Compiler und zu Schwierigkeiten für den Menschen führte, den Quellcode visuell korrekt zu analysieren. Das "do10i = ..." ist ein klassisches und berühmtes Beispiel. Mit der Weiterentwicklung der Sprachen wurden einige Regeln verschärft. Algol ist wahrscheinlich der Großvater der Standard-Faustregel "Bezeichner beginnen mit Buchstaben und können danach Buchstaben oder Zahlen haben".
Larry Gritz
FYI, der Microsoft BASIC-Interpreter, der die Grundlage für die gängigsten Microcomputer-Versionen von BASIC (einschließlich Applesoft Basic und Commodore Basic) bildete, konvertierte mithilfe eines Greedy-Tokens jede Zeichenfolge, die mit einem Sprachtoken übereinstimmt, in einen Byte-Wert mit dem High-Bit-Satz. Dies wurde ohne syntaktische Analyse durchgeführt. Wenn der Interpreter das Programm ausführt, geht er davon aus, dass alle gefundenen Buchstaben Teil eines Variablennamens sind.
Superkatze
1

Wahrscheinlich ist diese Konvention aus sehr frühen Entscheidungen über das Design historischer Sprachen hervorgegangen, da auf frühen Maschinen der gesamte Compiler einschließlich der lexikalischen Analyse in wenigen Kilowatt ausgeführt werden musste, weniger Speicher als selbst der Prozessor-Datencache der ersten Ebene auf aktuellen Mobilgeräten. Daher waren die zulässigen Variablennamen sehr begrenzt und mussten in sehr wenigen Op-Codes leicht von numerischen Konstanten zu unterscheiden sein.

So wurde die Konvention zu dem, was Generationen von Programmierern gewohnt sind.

hotpaw2
quelle
1

Es ist keine logisch vorgeschriebene Regel für die Programmiersprache, sondern nur die Konvention, die von vielen Sprachdesignern verwendet wird.

Ich kann eine radikal andere Sprache entwerfen, die alle Zeichen für Bezeichner zulässt. Für alle Codezeilen beschreiben die ersten 20 Zeichen den Anweisungstyp, die nächsten 20 Zeichen definieren das erste Symbol für die Anweisung und die nächsten 20 Zeichen sind Operanden für die Anweisung. Diese Sprache wird auf einem Stack-Prozessor ausgeführt.

01234567890123456789 01234567890123456789 01234567890123456789

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

Dieser Code könnte in C wie folgt übersetzt werden:

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

Das ist alles. Es ist bedeutungslos und die No-Number-In-Identifiers-Regel ist auch aus logischen Gründen sinnlos.

9dan
quelle
0

Neben "Convenience for the Lexer" ist meines Erachtens auch "Convenience for the Reader" zu berücksichtigen.

Wenn Sie Code lesen, müssen Sie schnell und wiederholt feststellen, welche Wörter Bezeichner und welche Zahlen sind. Die Suche nach einer Ziffer am Anfang erleichtert die visuelle Mustererkennung. Es wäre mühsam, wenn wir alle Charaktere sorgfältig prüfen müssten, um sicherzugehen.

kommendes Gewitter
quelle
0

Die Antwort auf diese Frage liegt in Automaten oder genauer gesagt in endlichen Automaten, die den regulären Ausdruck definieren. Die Regel ist ... Compiler benötigen genaue Algorithmen oder Regeln, um bei jedem Zeichen, das sie analysieren, zu entscheiden. Wenn Bezeichner mit einer Zahl beginnen dürfen, dann ist der Compiler in der Klemme ... über die Art des kommenden Tokens ... wird es eine Zahl oder ein Bezeichner sein ... und Compiler können nicht zu früheren Positionen zurückkehren. .so..um dem Compiler klar zu machen, dass das kommende Token genau ein Bezeichner oder eine Zahl ist ... diese Einschränkung gibt es ... coz von diesem ... Compiler weiß nur durch Scannen des ersten Zeichens, dass das kommende Token ist ist eine Kennung oder eine Zahl.

Waquas
quelle