2009-11-11 12 views
14

Ich bin gerade in der Go-FAQ auf this question gestoßen, und es hat mich an etwas erinnert, das mich schon seit einiger Zeit nervt. Leider sehe ich nicht wirklich, worauf die Antwort hinausläuft.Warum setzen viele Programmiersprachen den Typ * nach * dem Variablennamen?

Es scheint, wie fast jede nicht C-ähnliche Sprache, die Art nach dem Variablennamen setzt, etwa so:

var : int 

Nur aus purer Neugier, warum das so ist? Gibt es Vorteile, das eine oder das andere zu wählen?

+0

@Jurily - Ich werde das nicht bestreiten. Ich war nur neugierig, ob es einen Vorteil darin gibt, Dinge auf die eine oder andere Art zu tun. –

+3

@Jurily, um ganz genau zu sein, _everyone_ hat ihren Nachnamen zuletzt :-). Und um fair zu sein, denke ich, wenn die meisten Leute es unvoreingenommen betrachten würden, würden sie zustimmen, dass der allgemeine zu spezifische Ansatz (wie in Chinesisch oder, wie Sie sagen, Ungarisch) mehr Sinn macht. –

+0

https://stackoverflow.com/questions/1891775/any-reason-for-having-val-capacity-int-instead-of-val-int-capacity-in-scal/1893263 – starblue

Antwort

12

Es gibt ein Parsing-Problem, wie Keith Randall sagt, aber es ist nicht das, was er beschreibt. Das "Nicht-Wissen, ob es eine Deklaration oder ein Ausdruck ist" ist einfach egal - es interessiert Sie nicht, ob es ein Ausdruck oder eine Deklaration ist, bis Sie das Ganze überhaupt analysiert haben, an welchem ​​Punkt die Mehrdeutigkeit gelöst ist.

Bei Verwendung eines kontextfreien Parsers spielt es keine Rolle, ob der Typ vor oder nach dem Variablennamen steht. Es kommt darauf an, dass Sie keine benutzerdefinierten Typnamen nachschlagen müssen, um die Typspezifikation zu verstehen. Sie müssen nicht alles verstanden haben, was Sie vorher verstanden haben, um das aktuelle Token zu verstehen.

Pascal Syntax ist kontextfrei - wenn nicht vollständig, zumindest WRT dieses Problem. Die Tatsache, dass der Variablenname an erster Stelle steht, ist weniger wichtig als Details wie das Doppelpunkt-Trennzeichen und die Syntax der Typbeschreibungen.

C-Syntax ist kontextsensitiv.Damit der Parser bestimmen kann, wo eine Typbeschreibung endet und welches Token der Variablenname ist, muss er bereits alles vorher interpretiert haben, damit er feststellen kann, ob ein bestimmtes Bezeichner-Token der Variablenname oder nur ein anderes zu ihm beigetragenes Token ist die Typbeschreibung

Da die C-Syntax kontextsensitiv ist, ist es sehr schwierig (wenn nicht unmöglich), traditionelle Parser-Generator-Tools wie yacc/bison zu analysieren, während die Pascal-Syntax mit den gleichen Werkzeugen leicht zu parsen ist. Das heißt, es gibt jetzt Parser-Generatoren, die mit C und sogar C++ - Syntax umgehen können. Obwohl es nicht richtig dokumentiert ist oder in einer 1.? release etc, mein persönlicher Favorit ist Kelbt, die Backtracking LR verwendet und semantische "Rückgängig" unterstützt - im Grunde Ergänzungen der Symboltabelle rückgängig machen, wenn spekulative Paresen sich als falsch erweisen.

In der Praxis werden C- und C++ - Parser normalerweise handgeschrieben und mischen rekursive Abstiegs- und Präzedenzanalyse. Ich nehme an, das gilt auch für Java und C#.

Im Übrigen haben ähnliche Probleme mit Kontextsensitivität in C++ - Parsing viele Gemeinheiten verursacht. Die "Alternative Function Syntax" für C++ 0x arbeitet um ein ähnliches Problem, indem Sie eine Typspezifikation an das Ende verschieben und nach einem Trennzeichen platzieren - sehr ähnlich dem Pascal-Doppelpunkt für Funktionsrückgabetypen. Es wird die Kontextsensitivität nicht los, aber die Pascal-ähnliche Konvention macht es etwas überschaubarer.

+0

* EDIT * - Geständnis - mit rekursivem Abstieg ist es * etwas leichter zu parsen, wenn das Konstrukt nahe seinem Anfang leicht identifizierbar ist. Ein Schlüsselwort am Anfang ist dafür gut. Ich zähle jedoch nicht wirklich C type-then-variable-name, weil das Konstrukt früher als Variable-then-type nicht wirklich identifiziert wird. Es könnte eine Variablendeklaration sein - oder eine Funktionsdeklaration oder eine Funktionsart-Typumwandlung für eine Variable auf der linken Seite einer Zuweisung oder ... – Steve314

+0

Ist das nicht mehr eine Funktion des Doppelpunkts? ? Ich kann sagen, dass 'var: int' wegen des Doppelpunkts ein Variablenname ist. Es scheint, als wäre es genauso einfach, 'int: var' zu machen (wenn auch vielleicht ein bisschen hässlicher :-)). –

+1

@Jason Baker - Entschuldigung für die Verzögerung. Ein Compiler kann "varname int" oder "int varname" verarbeiten, wenn "int" ein Schlüsselwort ist. Allerdings ist "varname mytype" oder "mypype varname" eher ein Problem - nicht weil "mytype" genau eine Kennung ist, sondern weil Sie die Dictionary-Suche benötigen, um zu bestimmen, welche syntaktischen Konstrukte angewendet werden können. Die alte C-Syntax, bei der Strukturnamen keine gültigen Typnamen waren (Sie würden "struct structname myvar;" schreiben), war einfacher, da das "struct" -Präfix syntaktisch zeigt, dass der Typ * ein * -Typ ist. Ein Doppelpunkt kann helfen, aber es ist weder die ganze noch die einzige Lösung. – Steve314

1

So wurde die Sprache entworfen. Visual Basic war schon immer so.

Die meisten geschweiften Klammersprachen (wenn nicht alle) setzen den Typ zuerst. Dies ist für mich intuitiver, da dieselbe Position auch den Rückgabetyp einer Methode angibt. Die Eingaben werden also in Klammern gesetzt, und die Ausgabe wird hinter dem Methodennamen ausgegeben.

+4

Legages, die den Typ zuletzt auch setzen setze den Rückgabetyp zuletzt, zB name (arg: type): return_type –

+0

C++ 0x erlaubt es, die Funktion return type zuletzt zu setzen: 'auto func (int arg1) -> int' – jmucchiello

10

Die "meisten anderen" Sprachen, von denen Sie sprechen, sind diejenigen, die deklarativer sind. Sie zielen darauf ab, Ihnen zu erlauben, mehr entlang der Linien zu programmieren, in denen Sie denken (vorausgesetzt, Sie sind nicht in zwingendes Denken eingerahmt).

Typ zuletzt liest als ‚eine Variable erstellen Name des Typs TYPE genannten‘

dies ist das Gegenteil natürlich ‚erstellen TYPE Rufname‘ zu sagen, aber wenn man darüber nachdenkt, was der Wert für ist wichtiger als der Typ, der Typ ist lediglich eine programmatische Beschränkung auf die Daten

+1

+1 Gut gesagt ... und 15 weitere Charaktere. – wheaties

1

Ich bin mir nicht sicher, aber ich denke, es hat mit dem "Name vs. Nomen" -Konzept zu tun.

Im Wesentlichen, wenn Sie den Typ zuerst setzen (wie "int varname"), deklarieren Sie eine "Ganzzahl mit dem Namen 'varname'"; das heißt, Sie geben einer Instanz eines Typs einen Namen. Wenn Sie jedoch zuerst den Namen und dann den Typ eingeben (z. B. "varname: int"), sagen Sie "Dies ist 'varname'; es ist eine ganze Zahl". Im ersten Fall geben Sie einer Instanz von etwas einen Namen; in der zweiten definieren Sie ein Substantiv und erklären, dass es eine Instanz von etwas ist.

Es ist ein bisschen wie wenn Sie einen Tisch als ein Möbelstück definieren würden; "Das ist Möbel und ich nenne es" Tisch "" (Typ zuerst) ist anders als "ein Tisch ist eine Art von Möbeln" (Typ zuletzt) ​​zu sagen.

0

Wenn Sie den Typ zuerst verwenden, hilft das beim Parsen. Wenn Sie zum Beispiel in C, deklarierten Variablen wie

x int; 

Wenn Sie nur die x analysieren, dann wissen Sie nicht, ob x eine Erklärung oder ein Ausdruck ist. Im Gegensatz dazu mit

int x; 

Wenn Sie die int analysieren, wissen Sie, Sie in einer Erklärung sind (Typen immer eine Erklärung von einer Art beginnen).

Aufgrund des Fortschritts beim Parsen von Sprachen ist diese kleine Hilfe heutzutage nicht sonderlich nützlich.

+1

Wenn das Analysieren einfacher ist, würden Sie ein Schlüsselwort haben, das alle Variablendeklarationen einführt, z. B. var: 'var i: int' oder' var int i' – jmucchiello

+0

Und für benutzerdefinierte Typen (typedefs): xyz abc; - Welches ist der Typ und welcher ist der Name? Solange die Sprache den Regeln zustimmt, gibt es kein Problem - und wenn die Sprache sich nicht auf die Regeln einigen kann, ist sie wahrscheinlich nicht analysierbar. –

-3

Was ist mit dynamisch (cheers @ wcoenen) typisierte Sprachen? Sie verwenden nur die Variable.

+8

Sie meinen "dynamisch typisiert". "schwach gegen stark typisiert" ist nicht die gleiche Unterscheidung wie "statisch vs dynamisch typisiert". Zum Beispiel ist python dynamisch und stark typisierte Sprache: http://wiki.python.org/moin/Why%20is%20Python%20a%20dynamic%20language%20and%20also%20a%20strongly%20typed%20language –

+0

Schwach typisierte Sprachen wie awk oder (alt) Perl; oder Fortran mit seinen implizit typisierten Variablen - Namen, die mit I bis N beginnen, sind ganze Zahlen; der Rest ist REAL. –

7

Wenn der Name der Variablen in Spalte 0 beginnt, ist es einfacher, den Namen der Variablen zu finden.

Vergleichen

QHash<QString, QPair<int, QString> > hash; 

und

hash : QHash<QString, QPair<int, QString> >; 

Nun stell dir vor, wie viel besser lesbar typische C++ Header sein könnte.

+0

+1 Name ist besser lesbar, es zeigt Ihnen an, auf was die Anweisung gerade wirkt ... vorausgesetzt, Sie verwenden sinnvolle Variablennamen. – bobince

6

In der formalen Sprachtheorie und Typentheorie wird es fast immer als var: type geschrieben. Zum Beispiel in dem Lambda-Kalkül getippt werden Sie Beweise sehen enthalten Aussagen wie:

x : A y : B 
------------- 
\x.y : A->B 

Ich glaube nicht, es wirklich darauf ankommt, aber ich denke, es gibt zwei Begründungen: Die eine ist, dass „x: A“ gelesen wird "x ist vom Typ A", der andere ist, dass ein Typ wie ein Satz ist (zB int ist der Satz von ganzen Zahlen), und die Notation bezieht sich auf "x ε A".

Einige dieser Dinge sind älter als die modernen Sprachen, an die Sie denken.

8

Ein zunehmender Trend besteht darin, den Typ überhaupt nicht anzugeben oder den Typ optional anzugeben. Dies könnte eine dynamisch typisierte Sprache sein, in der es tatsächlich keinen Typ für die Variable gibt, oder es könnte eine statisch typisierte Sprache sein, die den Typ aus dem Kontext ableitet.

Wenn der Typ manchmal angegeben und manchmal abgeleitet wird, ist es einfacher zu lesen, wenn das optionale Bit danach kommt.

Es gibt auch Trends, die sich darauf beziehen, ob eine Sprache sich selbst als von der C-Schule oder der funktionalen Schule kommend oder was auch immer versteht, aber das ist Zeitverschwendung. Die Sprachen, die ihre Vorgänger verbessern und lernenswert sind, sind diejenigen, die bereit sind, Beiträge von allen verschiedenen Schulen basierend auf ihren Verdiensten zu akzeptieren und nicht wählerisch in Bezug auf das Erbe eines Merkmals zu sein.

0

Fortran setzt den Typ zuerst:

REAL*4 I,J,K 
INTEGER*4 A,B,C 

Und ja, es ist ein (sehr schwach) Witz es für diejenigen, die mit Fortran.

Es gibt Raum zu argumentieren, dass dies einfacher als C ist, die die Typinformation um den Namen herumgibt, wenn der Typ komplex genug ist (Zeiger auf Funktionen zum Beispiel).

+0

Sie haben FORFAN obfuscate erfunden. Gut gemacht. – jmucchiello

1

Ich dachte immer die Art und Weise C ist es etwas eigenartig: anstelle der Konstruktion von Typen muss der Benutzer sie implizit erklären. Es ist nicht nur vor/nach dem Variablennamen; Im Allgemeinen müssen Sie möglicherweise den Variablennamen unter den Typattributen einbetten (oder in einigen Fällen einen leeren Bereich einbetten, in dem der Name wäre, wenn Sie tatsächlich einen deklarieren würden).

Als eine schwache Form des Mustervergleichs ist es zu einem gewissen Grad verständlich, aber es scheint auch keine besonderen Vorteile zu bieten. Und wenn Sie versuchen, einen Funktionszeiger zu schreiben (oder zu lesen), können Sie leicht über den Punkt der leichten Verständlichkeit hinausgehen. Insgesamt ist dieser Aspekt von C ein Nachteil, und ich bin froh zu sehen, dass Go es hinter sich gelassen hat.

4

"Diejenigen, die sich nicht an die Vergangenheit erinnern können, sind dazu verdammt, sie zu wiederholen."

Den Typ vor der Variable zu setzen begann mit Fortran und Algol harmlos genug, aber es wurde wirklich hässlich in C, wo einige Modifikatoren vor der Variable, andere nach. Aus diesem Grund in C Sie solche Schönheiten wie

int (*p)[10]; 

oder

void (*signal(int x, void (*f)(int)))(int) 

zusammen mit einem Dienstprogramm (cdecl), deren Zweck haben, ist eine solche Kauderwelsch zu entschlüsseln.

In Pascal, kommt die Art nach den Variablen, so dass die ersten Beispiele werden

p: pointer to array[10] of int 

Contrast mit

q: array[10] of pointer to int 

, die in C, ist

int *q[10] 

In C , benötigen Sie Klammern, um dies von int (* p) [10] zu unterscheiden. In Pascal sind keine Klammern erforderlich, wenn nur die Reihenfolge zählt.

würde die Signalfunktion

signal: function(x: int, f: function(int) to void) to (function(int) to void) 

Noch ein Schluck, aber zumindest im Bereich des menschlichen Fassungsvermögens sein.

Fairerweise ist das Problem nicht, dass C die Typen vor den Namen stellt, sondern dass es pervers darauf besteht, Bits und Stücke vor und nach dem Namen zu setzen.

Aber wenn Sie versuchen, alles vor dem Namen zu setzen, ist der Auftrag noch nicht intuitiv:

int [10] a // an int, ahem, ten of them, called a 
int [10]* a // an int, no wait, ten, actually a pointer thereto, called a 

So lautet die Antwort: Ein vernünftig entworfen Programmiersprache die Variablen vor dem Typ setzt, weil das Ergebnis mehr ist lesbar für Menschen.

Verwandte Themen