Aus dem Java 5- Sprachführer :
Wenn Sie den Doppelpunkt (:) sehen, lesen Sie ihn als "in".
Warum dann gar nicht erst verwenden in
?
Das nervt mich seit Jahren. Weil es nicht mit dem Rest der Sprache übereinstimmt. Zum Beispiel in Java gibt es implements
, extends
, super
für die Beziehungen zwischen den Typen anstelle von Symbolen wie in C ++, Scala oder Ruby.
In Java Doppelpunkt in 5 Kontexten verwendet . Drei davon sind von C. geerbt. Die anderen beiden wurden von Joshua Bloch gebilligt. Zumindest sagte er dies während des Gesprächs über die Kontroverse um die Schließung . Dies tritt auf, wenn er die Verwendung eines Doppelpunkts für die Zuordnung als inkonsistent mit jeder Semantik kritisiert. Was mir seltsam erscheint, weil es die für jeden Missbrauch erwarteten Muster sind. Wie list_name/category: elements
oder laberl/term: meaning
.
Ich habe mich in jcp und jsr umgesehen, aber keine Anzeichen einer Mailingliste gefunden. Keine Diskussionen zu diesem Thema von Google gefunden. Nur Neulinge verwirrt durch die Bedeutung von Doppelpunkt in for
.
Hauptargumente gegen bisher in
geliefert:
- erfordert neues Schlüsselwort; und
- erschwert das lexen.
Schauen wir uns relevante Grammatikdefinitionen an :
Erklärung : 'for' '(' forControl ')' Anweisung | ... ;; forControl : advancedForControl | forInit? ';' Ausdruck? ';' forUpdate? ;; EnhancedForControl : variableModifier * Typ variableDeclaratorId ':' Ausdruck ;;
Ändern Sie von :
, in
um keine zusätzliche Komplexität zu erzielen, oder erfordern Sie ein neues Schlüsselwort.
Antworten:
Normale Parser, wie sie im Allgemeinen gelehrt werden, haben eine Lexerstufe, bevor der Parser die Eingabe berührt. Der Lexer (auch "Scanner" oder "Tokenizer") zerlegt die Eingabe in kleine Token, die mit einem Typ versehen sind. Auf diese Weise kann der Hauptparser Token als Terminalelemente verwenden, anstatt jedes Zeichen als Terminal behandeln zu müssen, was zu spürbaren Effizienzgewinnen führt. Insbesondere kann der Lexer auch alle Kommentare und Leerzeichen entfernen. Eine separate Tokenizer-Phase bedeutet jedoch, dass Schlüsselwörter nicht auch als Bezeichner verwendet werden können (es sei denn, die Sprache unterstützt das Abstreifen, das etwas in Ungnade gefallen ist, oder stellt allen Bezeichnern ein Siegel wie vor
$foo
).Warum? Nehmen wir an, wir haben einen einfachen Tokenizer, der die folgenden Token versteht:
Der Tokenizer stimmt immer mit dem längsten Token überein und bevorzugt Schlüsselwörter gegenüber Bezeichnern. Also
interesting
wird lexed alsIDENT:interesting
, aberin
wird lexed alsIN
, niemals alsIDENT:interesting
. Ein Code-Snippet wiewird in den Token-Stream übersetzt
Bisher funktioniert das. Aber jede Variable
in
würde als Schlüsselwort lexiert undIN
nicht als Variable, die den Code beschädigen würde. Der Lexer behält keinen Status zwischen den Token bei und kann nicht wissen, dass diesin
normalerweise eine Variable sein sollte, außer wenn wir uns in einer for-Schleife befinden. Außerdem sollte der folgende Code legal sein:Der erste
in
wäre eine Kennung, der zweite ein Schlüsselwort.Es gibt zwei Reaktionen auf dieses Problem:
Kontextbezogene Schlüsselwörter sind verwirrend. Verwenden wir stattdessen Schlüsselwörter.
Java hat viele reservierte Wörter, von denen einige nur dazu dienen, Programmierern, die von C ++ zu Java wechseln, hilfreichere Fehlermeldungen zukommen zu lassen. Durch das Hinzufügen neuer Schlüsselwörter wird der Code unterbrochen. Das Hinzufügen von kontextbezogenen Schlüsselwörtern ist für einen Leser des Codes verwirrend, es sei denn, sie verfügen über eine gute Syntaxhervorhebung, und die Implementierung von Tools ist schwierig, da fortgeschrittenere Analysetechniken verwendet werden müssen (siehe unten).
Wenn wir die Sprache erweitern möchten, besteht der einzig vernünftige Ansatz darin, Symbole zu verwenden, die zuvor in der Sprache nicht legal waren. Insbesondere können dies keine Bezeichner sein. Mit der foreach-Schleifensyntax hat Java das vorhandene
:
Schlüsselwort mit einer neuen Bedeutung wiederverwendet . Mit Lambdas fügte Java ein->
Schlüsselwort hinzu, das zuvor in keinem legalen Programm vorkommen konnte (-->
würde immer noch als legal lexiert'--' '>'
und->
möglicherweise zuvor als lexiert'-', '>'
, aber diese Sequenz würde vom Parser abgelehnt).Kontextbezogene Schlüsselwörter vereinfachen Sprachen, lassen Sie uns sie implementieren
Lexer sind unbestreitbar nützlich. Aber anstatt einen Lexer vor dem Parser auszuführen, können wir sie zusammen mit dem Parser ausführen. Bottom-up-Parser kennen immer die Token-Typen, die an einem bestimmten Ort akzeptabel sind. Der Parser kann dann den Lexer auffordern, einen dieser Typen an der aktuellen Position abzugleichen. In einer for-each-Schleife befindet sich der Parser an der Position, die
·
in der (vereinfachten) Grammatik angegeben ist, nachdem die Variable gefunden wurde:An dieser Stelle sind die legalen Token
SEMICOLON
oderIN
, aber nichtIDENT
. Ein Schlüsselwortin
wäre völlig eindeutig.In diesem speziellen Beispiel hätten Top-Down-Parser auch kein Problem, da wir die obige Grammatik umschreiben können
und alle für die Entscheidung notwendigen Token können ohne Rückverfolgung angezeigt werden.
Betrachten Sie die Benutzerfreundlichkeit
Java tendierte immer zur semantischen und syntaktischen Einfachheit. Zum Beispiel unterstützt die Sprache das Überladen von Operatoren nicht, da dies den Code weitaus komplizierter machen würde. Wenn wir uns also zwischen
in
und:
für eine für jede Schleife bestimmte Syntax entscheiden, müssen wir berücksichtigen, welche weniger verwirrend und für Benutzer offensichtlicher ist. Der Extremfall wäre wahrscheinlich(Hinweis: Java verfügt über separate Namespaces für Typnamen, Variablen und Methoden. Ich denke, dies war meistens ein Fehler. Dies bedeutet nicht, dass das spätere Sprachdesign weitere Fehler hinzufügen muss .)
Welche Alternative bietet klarere visuelle Trennungen zwischen der Iterationsvariablen und der iterierten Sammlung? Welche Alternative erkennt man schneller, wenn man sich den Code ansieht? Ich habe festgestellt, dass das Trennen von Symbolen bei diesen Kriterien besser ist als eine Wortfolge. Andere Sprachen haben andere Werte. Zum Beispiel formuliert Python viele Operatoren auf Englisch, damit sie natürlich gelesen werden können und leicht zu verstehen sind. Dieselben Eigenschaften können es jedoch ziemlich schwierig machen, ein Stück Python auf einen Blick zu verstehen.
quelle
Die for-each-Schleifensyntax wurde in Java 5 hinzugefügt. Sie müssten
in
ein Sprachschlüsselwort erstellen, und das spätere Hinzufügen von Schlüsselwörtern zu einer Sprache ist etwas, das Sie um jeden Preis vermeiden, da es den vorhandenen Code beschädigt - plötzlich verursachen alle genannten Variablenin
eine Analyse Error.enum
war in dieser Hinsicht schon schlimm genug.quelle
in
hätte also bedeutet, entweder ein neues Schlüsselwort einzuführen und damit die Abwärtskompatibilität zu brechen (System.in
irgendjemand?) Oder ein zuvor unbekanntes brandneues Konzept einzuführen (kontextbezogene Schlüsselwörter). Alles für welchen Gewinn?for(variable in expression)
niemals mit einem Rechtscode mehrdeutig sein, selbst wenn "in" für Variablen verwendet werden kann. Eine separate Lexer-Phase ist jedoch in vielen Compiler-Toolchains weit verbreitet. Dies würde es unmöglich oder zumindest weitaus schwieriger machen, Java mit einigen gängigen Parser-Generatoren zu analysieren. Die Syntax einer Sprache einfach zu halten, ist normalerweise für alle Beteiligten gut. Nicht jeder braucht syntaktische Monstrositäten wie C ++ oder Perl.const
undgoto
sind beide reservierte Wörter in Java, werden aber (noch) nicht verwendet.