Ich frage in Bezug auf c #, aber ich gehe davon aus, dass es in den meisten anderen Sprachen dasselbe ist.
Hat jemand eine gute Definition von Ausdrücken und Aussagen und was sind die Unterschiede?
Ich frage in Bezug auf c #, aber ich gehe davon aus, dass es in den meisten anderen Sprachen dasselbe ist.
Hat jemand eine gute Definition von Ausdrücken und Aussagen und was sind die Unterschiede?
Antworten:
Ausdruck: Etwas, das einen Wert ergibt. Beispiel: 1 + 2 / x
Anweisung: Eine Codezeile, die etwas bewirkt. Beispiel: GOTO 100
In den frühesten universellen Programmiersprachen wie FORTRAN war die Unterscheidung glasklar. In FORTRAN war eine Anweisung eine Ausführungseinheit, eine Sache, die Sie getan haben. Der einzige Grund, warum es nicht als "Linie" bezeichnet wurde, war, dass es manchmal mehrere Linien umfasste. Ein Ausdruck allein konnte nichts tun ... Sie mussten ihn einer Variablen zuweisen.
1 + 2 / X
ist ein Fehler in FORTRAN, weil es nichts tut. Mit diesem Ausdruck musste man etwas anfangen:
X = 1 + 2 / X
FORTRAN hatte keine Grammatik, wie wir sie heute kennen - diese Idee wurde zusammen mit der Backus-Naur-Form (BNF) als Teil der Definition von Algol-60 erfunden. Zu diesem Zeitpunkt war die semantische Unterscheidung ("einen Wert haben" gegenüber "etwas tun") in der Syntax verankert : Eine Art von Phrase war ein Ausdruck und eine andere eine Aussage, und der Parser konnte sie unterscheiden.
Designer späterer Sprachen verwischten die Unterscheidung: Sie erlaubten syntaktischen Ausdrücken, Dinge zu tun, und sie erlaubten syntaktische Anweisungen, die Werte hatten. Das früheste Beispiel für eine populäre Sprache, das noch erhalten ist, ist C. Die Designer von C haben erkannt, dass kein Schaden angerichtet wurde, wenn Sie einen Ausdruck bewerten und das Ergebnis wegwerfen durften. In C kann jeder syntaktische Ausdruck zu einer Aussage gemacht werden, indem am Ende ein Semikolon angeheftet wird:
1 + 2 / x;
ist eine absolut legitime Aussage, obwohl absolut nichts passieren wird. In ähnlicher Weise kann ein Ausdruck in C Nebenwirkungen haben - er kann etwas ändern.
1 + 2 / callfunc(12);
weil callfunc
vielleicht nur etwas Nützliches tun.
Sobald Sie zulassen, dass ein Ausdruck eine Anweisung ist, können Sie auch den Zuweisungsoperator (=) in Ausdrücken zulassen. Deshalb können Sie mit C Dinge wie tun
callfunc(x = 2);
Dies wertet den Ausdruck x = 2 aus (weist x den Wert 2 zu) und übergibt diesen (die 2) dann an die Funktion callfunc
.
Diese Unschärfe von Ausdrücken und Anweisungen tritt in allen C-Derivaten (C, C ++, C # und Java) auf, die noch einige Anweisungen (wie while
) enthalten, aber die Verwendung fast aller Ausdrücke als Anweisung ermöglichen (nur in C # -Zuweisung). Aufruf-, Inkrement- und Dekrementausdrücke können als Anweisungen verwendet werden (siehe Antwort von Scott Wisniewski ).
Das Vorhandensein von zwei "syntaktischen Kategorien" (der technische Name für die Art von Aussagen und Ausdrücken) kann zu Doppelarbeit führen. Zum Beispiel hat C zwei Formen von Bedingungen, die Anweisungsform
if (E) S1; else S2;
und die Ausdrucksform
E ? E1 : E2
Und manchmal möchten Benutzer eine Duplizierung, die nicht vorhanden ist: In Standard C kann beispielsweise nur eine Anweisung eine neue lokale Variable deklarieren. Diese Funktion ist jedoch so nützlich, dass der GNU C-Compiler eine GNU-Erweiterung bereitstellt, mit der ein Ausdruck a deklarieren kann lokale Variable auch.
Designer anderer Sprachen mochten diese Art der Vervielfältigung nicht und sie sahen früh, dass die syntaktische Unterscheidung zwischen Anweisungen und Ausdrücken nicht allzu nützlich ist , wenn Ausdrücke sowohl Nebenwirkungen als auch Werte haben können - also haben sie sie beseitigt . Haskell, Icon, Lisp und ML sind alle Sprachen, die keine syntaktischen Anweisungen haben - sie haben nur Ausdrücke. Sogar die klassenstrukturierten Schleifen und bedingten Formen werden als Ausdrücke betrachtet und haben Werte - aber keine sehr interessanten.
callfunc(x = 2);
geht x
es weiter callfunc
, nicht 2
. Wenn x
es sich um einen Float handelt, callfunc(float)
wird nicht aufgerufen callfunc(int)
. Und wenn Sie in C ++ x=y
an übergeben func
und func
eine Referenz nehmen und ändern, ändert x
sich dies nicht y
.
where
Klausel in haskell als Ausdruck und nicht als Aussage betrachtet wird. learnyouahaskell.com/syntax-in-functions#where
where
ist tatsächlich ein Teil der Funktionsdeklaration, nicht Ausdruck oder Anweisung.
Beachten Sie, dass "=" in C tatsächlich ein Operator ist, der zwei Dinge tut:
Hier ist ein Auszug aus der ANSI C-Grammatik. Sie können sehen, dass C nicht viele verschiedene Arten von Anweisungen hat ... Die meisten Anweisungen in einem Programm sind Ausdrucksanweisungen, dh ein Ausdruck mit einem Semikolon am Ende.
statement
: labeled_statement
| compound_statement
| expression_statement
| selection_statement
| iteration_statement
| jump_statement
;
expression_statement
: ';'
| expression ';'
;
Ein Ausdruck gibt einen Wert zurück, eine Anweisung jedoch nicht.
Zum Beispiel:
1 + 2 * 4 * foo.bar() //Expression
foo.voidFunc(1); //Statement
Die große Sache zwischen den beiden ist, dass Sie Ausdrücke miteinander verketten können, während Anweisungen nicht verkettet werden können.
foo.voidFunc(1);
ist ein Ausdruck mit einem ungültigen Wert. while
und if
sind Aussagen.
return
wird als Untertreibung angesehen.
Sie finden dies auf Wikipedia , aber Ausdrücke werden mit einem bestimmten Wert ausgewertet, während Anweisungen keinen ausgewerteten Wert haben.
Ausdrücke können also in Anweisungen verwendet werden, aber nicht umgekehrt.
Beachten Sie, dass einige Sprachen (wie Lisp und ich glaube Ruby und viele andere) nicht zwischen Aussage und Ausdruck unterscheiden ... in solchen Sprachen ist alles ein Ausdruck und kann mit anderen Ausdrücken verkettet werden.
Für eine Erklärung wichtiger Unterschiede in der Zusammensetzbarkeit (Verkettbarkeit) von Ausdrücken gegenüber Aussagen ist meine Lieblingsreferenz John Backus 'Turing-Preispapier: Kann Programmierung vom von Neumann-Stil befreit werden? .
Imperative Sprachen (Fortran, C, Java, ...) betonen Aussagen zur Strukturierung von Programmen und haben Ausdrücke als eine Art Nachdenken. Funktionssprachen betonen Ausdrücke. Rein funktionale Sprachen haben so mächtige Ausdrücke, dass Aussagen insgesamt eliminiert werden können.
Einfach: Ein Ausdruck ergibt einen Wert, eine Anweisung nicht.
{}
ist eine Aussage. Das Wort in Angstzitate zu setzen, ändert daran nichts. Anweisungen sind syntaktische Konstrukte mit Semantik. Es gibt keine "Semantik-Ebene" - Sie scheinen sich auf die Ausführung zu beziehen . Sie sagen, Sie versuchen, genau zu sein, aber Sie haben daran versagt. Ihre Beschwerde über "die Unwissenheit der Wähler" ist rein ad hominem; Sie haben keine Informationen über die mentalen Zustände der Downvoter.
{}
wird als Anweisung in der C # -Sprachenspezifikation definiert.
Ausdrücke können ausgewertet werden, um einen Wert zu erhalten, während Anweisungen keinen Wert zurückgeben (sie sind vom Typ void ).
Funktionsaufrufausdrücke können natürlich auch als Anweisungen betrachtet werden. Wenn die Ausführungsumgebung jedoch nicht über eine spezielle integrierte Variable verfügt, die den zurückgegebenen Wert enthält, kann dieser nicht abgerufen werden.
Anweisungsorientierte Sprachen erfordern, dass alle Prozeduren eine Liste von Anweisungen sind. Ausdrucksorientierte Sprachen, bei denen es sich wahrscheinlich um alle funktionalen Sprachen handelt, sind Listen von Ausdrücken oder im Fall von LISP ein langer S-Ausdruck, der eine Liste von Ausdrücken darstellt.
Obwohl beide Typen zusammengesetzt werden können, können die meisten Ausdrücke beliebig zusammengesetzt werden, solange die Typen übereinstimmen. Jeder Anweisungstyp hat seine eigene Art, andere Anweisungen zu erstellen, wenn sie das alles können. Foreach- und if-Anweisungen erfordern entweder eine einzelne Anweisung oder dass alle untergeordneten Anweisungen nacheinander in einem Anweisungsblock abgelegt werden, es sei denn, die Unteranweisungen lassen ihre eigenen Unteranweisungen zu.
Anweisungen können auch Ausdrücke enthalten, wobei ein Ausdruck keine Anweisungen enthält. Eine Ausnahme wäre jedoch ein Lambda-Ausdruck, der eine Funktion darstellt und daher alles enthalten kann, was eine Funktion enthalten kann, es sei denn, die Sprache erlaubt nur begrenzte Lambdas, wie Pythons Lambdas mit einem Ausdruck.
In einer ausdrucksbasierten Sprache benötigen Sie lediglich einen einzelnen Ausdruck für eine Funktion, da alle Kontrollstrukturen einen Wert zurückgeben (viele von ihnen geben NIL zurück). Eine return-Anweisung ist nicht erforderlich, da der zuletzt ausgewertete Ausdruck in der Funktion der Rückgabewert ist.
Void
ist nicht der unterste Typ. Siehe meine Antwort .
null
)? Wäre nicht void
mehr wie der Einheitentyp (aber mit seinem Einzelwert unzugänglich)?
void
es sich um den Rückgabetyp einer Funktion handelt, die niemals zurückgegeben wird (z. B. eine throw
fehlerhafte Funktion), handelt es sich um den unteren Typ . Ansonsten void
ist der Einheitentyp . Sie haben Recht, dass eine Anweisung, die nicht abweichen kann, den Einheitentyp hat. Aber eine Aussage, die abweichen kann, ist der unterste Typ. Aufgrund des Halting-Theorems können wir normalerweise nicht beweisen, dass eine Funktion nicht divergiert, daher denke ich, dass Einheit Fiktion ist. Der untere Typ kann keinen Wert haben, daher kann er keinen einzigen Wert von haben null
.
null
Wert ist wirklich ein Pseudowert, der angibt, dass sich eine Referenz auf etwas bezieht, das nicht existiert.
Einige Dinge über ausdrucksbasierte Sprachen:
Am wichtigsten: Alles gibt einen Wert zurück
Es gibt keinen Unterschied zwischen geschweiften Klammern und geschweiften Klammern zum Abgrenzen von Codeblöcken und Ausdrücken, da alles ein Ausdruck ist. Dies verhindert jedoch nicht das lexikalische Scoping: Eine lokale Variable könnte beispielsweise für den Ausdruck definiert werden, in dem ihre Definition enthalten ist, sowie für alle darin enthaltenen Anweisungen.
In einer ausdrucksbasierten Sprache gibt alles einen Wert zurück. Das kann zunächst etwas seltsam sein - Was kommt (FOR i = 1 TO 10 DO (print i))
zurück?
Einige einfache Beispiele:
(1)
kehrt zurück 1
(1 + 1)
kehrt zurück 2
(1 == 1)
kehrt zurück TRUE
(1 == 2)
kehrt zurück FALSE
(IF 1 == 1 THEN 10 ELSE 5)
kehrt zurück 10
(IF 1 == 2 THEN 10 ELSE 5)
kehrt zurück 5
Ein paar komplexere Beispiele:
OpenADoor(), FlushTheToilet()
oder TwiddleYourThumbs()
einen alltäglichen Wert zurückgeben, z. B. OK, Fertig oder Erfolg.(FOR i = 1 TO 10 DO (print i))
Der Wert der for-Schleife ist "10". Der (print i)
Ausdruck wird zehnmal ausgewertet, wobei jedes Mal i als Zeichenfolge zurückgegeben wird. Das letzte Mal durch Rückkehr 10
, unsere endgültige AntwortEs erfordert oft eine leichte Änderung der Denkweise, um das Beste aus einer ausdrucksbasierten Sprache herauszuholen, da die Tatsache, dass alles ein Ausdruck ist, es ermöglicht, viele Dinge zu „inline“
Als schnelles Beispiel:
FOR i = 1 to (IF MyString == "Hello, World!" THEN 10 ELSE 5) DO ( LotsOfCode )
ist ein vollkommen gültiger Ersatz für das nicht ausdrucksbasierte
IF MyString == "Hello, World!" THEN TempVar = 10 ELSE TempVar = 5 FOR i = 1 TO TempVar DO ( LotsOfCode )
In einigen Fällen fühlt sich das Layout, das ausdrucksbasierter Code zulässt, für mich viel natürlicher an
Dies kann natürlich zum Wahnsinn führen. Im Rahmen eines Hobbyprojekts in einer ausdrucksbasierten Skriptsprache namens MaxScript gelang es mir, diese Monsterlinie zu entwickeln
IF FindSectionStart "rigidifiers" != 0 THEN FOR i = 1 TO (local rigidifier_array = (FOR i = (local NodeStart = FindsectionStart "rigidifiers" + 1) TO (FindSectionEnd(NodeStart) - 1) collect full_array[i])).count DO
(
LotsOfCode
)
Eine Anweisung ist ein Sonderfall eines Ausdrucks mit void
Typ. Die Tendenz der Sprachen, Aussagen anders zu behandeln, verursacht häufig Probleme, und es wäre besser, wenn sie richtig verallgemeinert würden.
Zum Beispiel haben wir in C # die sehr nützliche Func<T1, T2, T3, TResult>
überladene Menge generischer Delegaten. Wir müssen aber auch einen entsprechenden Action<T1, T2, T3>
Satz haben, und die allgemeine Programmierung höherer Ordnung muss ständig dupliziert werden, um mit dieser unglücklichen Gabelung fertig zu werden.
Triviales Beispiel - Eine Funktion, die prüft, ob eine Referenz null ist, bevor eine andere Funktion aufgerufen wird:
TResult IfNotNull<TValue, TResult>(TValue value, Func<TValue, TResult> func)
where TValue : class
{
return (value == null) ? default(TValue) : func(value);
}
Könnte der Compiler mit der Möglichkeit des TResult
Seins umgehen void
? Ja. Alles, was es tun muss, ist zu verlangen, dass auf return ein Ausdruck vom Typ folgt void
. Das Ergebnis von default(void)
wäre vom Typ void
, und die übergebene Funktion müsste von der Form sein Func<TValue, void>
(die äquivalent wäre Action<TValue>
).
Eine Reihe anderer Antworten implizieren, dass Sie Aussagen nicht wie Ausdrücke mit Ausdrücken verketten können, aber ich bin mir nicht sicher, woher diese Idee kommt. Wir können uns das ;
, was nach Anweisungen erscheint, als einen binären Infix-Operator vorstellen, der zwei Typausdrücke nimmt void
und sie zu einem einzigen Typausdruck kombiniert void
.
Anweisungen -> Anweisungen, die nacheinander ausgeführt werden sollen
Ausdrücke -> Auswertung, die einen Wert zurückgibt
Anweisungen sind im Grunde genommen wie Schritte oder Anweisungen in einem Algorithmus. Das Ergebnis der Ausführung einer Anweisung ist die Aktualisierung des Anweisungszeigers (im Assembler so genannt).
Ausdrücke implizieren keine Ausführungsreihenfolge auf den ersten Blick, sondern dienen dazu, einen Wert zu bewerten und zurückzugeben. In den imperativen Programmiersprachen hat die Bewertung eines Ausdrucks eine Reihenfolge, aber es liegt nur am imperativen Modell, aber es ist nicht ihre Essenz.
Beispiele für Aussagen:
for
goto
return
if
(Alle implizieren den Fortschritt der Ausführungszeile (Anweisung) zu einer anderen Zeile.)
Beispiel für Ausdrücke:
2+2
(es impliziert nicht die Idee der Ausführung, sondern der Bewertung)
Aussage ,
Eine Anweisung ist ein prozeduraler Baustein, aus dem alle C # -Programme erstellt werden. Eine Anweisung kann eine lokale Variable oder Konstante deklarieren, eine Methode aufrufen, ein Objekt erstellen oder einer Variablen, Eigenschaft oder einem Feld einen Wert zuweisen.
Eine Reihe von Anweisungen, die von geschweiften Klammern umgeben sind, bilden einen Codeblock. Ein Methodenkörper ist ein Beispiel für einen Codeblock.
bool IsPositive(int number)
{
if (number > 0)
{
return true;
}
else
{
return false;
}
}
Anweisungen in C # enthalten häufig Ausdrücke. Ein Ausdruck in C # ist ein Codefragment, das einen Literalwert, einen einfachen Namen oder einen Operator und seine Operanden enthält.
Ausdruck ,
Ein Ausdruck ist ein Codefragment, das zu einem einzelnen Wert, Objekt, einer Methode oder einem Namespace ausgewertet werden kann. Die zwei einfachsten Arten von Ausdrücken sind Literale und einfache Namen. Ein Literal ist ein konstanter Wert ohne Namen.
int i = 5;
string s = "Hello World";
Sowohl i als auch s sind einfache Namen, die lokale Variablen identifizieren. Wenn diese Variablen in einem Ausdruck verwendet werden, wird der Wert der Variablen abgerufen und für den Ausdruck verwendet.
if(number >= 0) return true; else return false;
oder noch besser bool? IsPositive(int number) { if(number > 0) return true; else if(number < 0) return false; else return null;}
:)
Ich bevorzuge die Bedeutung statement
im formalen logischen Sinne des Wortes. Es ist eines, das den Status einer oder mehrerer Variablen in der Berechnung ändert und es ermöglicht, eine wahre oder falsche Aussage über deren Wert (e) zu treffen.
Ich denke, es wird immer Verwirrung in der Computerwelt und in der Wissenschaft im Allgemeinen geben, wenn neue Terminologie oder Wörter eingeführt werden, vorhandene Wörter "neu verwendet" werden oder Benutzer die vorhandene, etablierte oder "richtige" Terminologie für das, was sie beschreiben, nicht kennen
Ich bin mit keiner der Antworten hier wirklich zufrieden. Ich habe mir die Grammatik für C ++ (ISO 2008) angesehen . Vielleicht reichen die Antworten jedoch aus Gründen der Didaktik und Programmierung aus, um die beiden Elemente zu unterscheiden (die Realität sieht jedoch komplizierter aus).
Eine Anweisung besteht aus null oder mehr Ausdrücken, kann aber auch andere Sprachkonzepte sein. Dies ist das Extended Backus Naur-Formular für die Grammatik (Auszug zur Aussage):
statement:
labeled-statement
expression-statement <-- can be zero or more expressions
compound-statement
selection-statement
iteration-statement
jump-statement
declaration-statement
try-block
Wir können die anderen Konzepte sehen, die in C ++ als Anweisungen betrachtet werden.
case
Zum Beispiel ist eine beschriftete Anweisungif
if/else
,case
while
, do...while
,for (...)
break
, continue
, return
(kann Ausdruck zurück),goto
try/catch
Blöcke darstelltDies ist ein Auszug, der den Ausdrucksteil zeigt:
expression:
assignment-expression
expression "," assignment-expression
assignment-expression:
conditional-expression
logical-or-expression assignment-operator initializer-clause
throw-expression
+
, -
, *
, /
, &
, |
, &&
, ||
, ...)throw
Klausel ist auch ein AusdruckAussagen sind grammatikalisch vollständige Sätze. Ausdrücke sind nicht. Zum Beispiel
x = 5
liest als "x bekommt 5." Dies ist ein vollständiger Satz. Der Code
(x + 5)/9.0
liest, "x plus 5 alle geteilt durch 9.0." Dies ist kein vollständiger Satz. Die Aussage
while k < 10:
print k
k += 1
ist ein vollständiger Satz. Beachten Sie, dass der Loop-Header nicht ist. "while k <10" ist ein Nebensatz.
while
ist ein Ausdruck ist einige Sprachen wie Scala. Sie verbinden Grammatik mit Tippen. Siehe meine Antwort .
while
mit einem Körper ist immer noch ein Ausdruck in Scala. Es kann auch eine Aussage sein, wenn es Nebenwirkungen hervorruft, was meine stark herabgestimmte Antwort erlaubt (ein Ausdruck kann auch eine Aussage sein). Meine Antwort ist die einzig richtige. Entschuldigung an alle Leser, die nicht verstehen können.
(x + 5)/9.0
kann definitiv allein als Aussage stehen. Wenn Sie mit grammatikalisch vollständig ein gültiges Programm meinen, erlaubt C nicht, dass Anweisungen als einzelnes Programm allein stehen.
Hier ist der Sommer einer der einfachsten Antworten, die ich gefunden habe.
ursprünglich beantwortet von Anders Kaseorg
Eine Anweisung ist eine vollständige Codezeile, die eine Aktion ausführt, während ein Ausdruck ein beliebiger Abschnitt des Codes ist, der einen Wert ergibt.
Ausdrücke können mithilfe von Operatoren „horizontal“ zu größeren Ausdrücken kombiniert werden, während Anweisungen nur „vertikal“ durch Schreiben nacheinander oder mit Blockkonstrukten kombiniert werden können.
Jeder Ausdruck kann als Anweisung verwendet werden (deren Wirkung darin besteht, den Ausdruck auszuwerten und den resultierenden Wert zu ignorieren), aber die meisten Anweisungen können nicht als Ausdrücke verwendet werden.
Die De-facto-Basis dieser Konzepte ist:
Ausdrücke : Eine syntaktische Kategorie, deren Instanz zu einem Wert ausgewertet werden kann.
Anweisung : Eine syntaktische Kategorie, deren Instanz an der Auswertung eines Ausdrucks beteiligt sein kann, und der resultierende Wert der Auswertung (falls vorhanden) sind nicht garantiert verfügbar.
Abgesehen von dem allerersten Kontext für FORTRAN in den frühen Jahrzehnten sind sowohl Definitionen von Ausdrücken als auch Aussagen in der akzeptierten Antwort offensichtlich falsch:
sizeof
Operators verwendet wird, niemals ausgewertet.(Übrigens möchte ich dieser Antwort in Bezug auf Materialien zu C [Zitieren erforderlich] hinzufügen, da ich mich nicht erinnern kann, ob DMR solche Meinungen hat. Es scheint nicht, sonst sollte es keine Gründe geben, die Funktionsduplizierung im Design von C beizubehalten : insbesondere der Kommaoperator im Vergleich zu den Anweisungen.)
(Die folgende Begründung ist nicht die direkte Antwort auf die ursprüngliche Frage, aber ich halte es für notwendig, etwas zu klären, das hier bereits beantwortet wurde.)
Es ist jedoch zweifelhaft, dass wir eine bestimmte Kategorie von "Anweisungen" in allgemeinen Programmiersprachen benötigen:
begin
im Schema) oder einen syntaktischen Zucker monadischer Strukturen ersetzt werden.++i + ++i
in C bedeutungslosen Punkt zu erreichen .)Warum also Aussagen? Wie auch immer, die Geschichte ist schon ein Chaos. Es scheint, dass die meisten Sprachdesigner ihre Wahl nicht sorgfältig treffen.
Schlimmer noch, es gibt sogar einigen Typsystem-Enthusiasten (die mit der PL-Geschichte nicht genug vertraut sind) einige Missverständnisse, dass Typsysteme wichtige Dinge mit den grundlegenderen Entwürfen von Regeln für die Betriebssemantik zu tun haben müssen.
Im Ernst, das Denken in Abhängigkeit von den Typen ist in vielen Fällen nicht so schlecht, aber in diesem speziellen Fall besonders nicht konstruktiv. Sogar Experten können es vermasseln.
Zum Beispiel betont jemand die gut typisierende Natur als zentrales Argument gegen die traditionelle Behandlung von unbegrenzten Fortsetzungen . Obwohl die Schlussfolgerung einigermaßen vernünftig ist und die Einsichten über zusammengesetzte Funktionen in Ordnung sind ( aber für die Essenz immer noch viel zu naiv ), ist dieses Argument nicht stichhaltig, da es den "Seitenkanal" -Ansatz in der Praxis wie _Noreturn any_of_returnable_types
(in C11) zum Codieren völlig ignoriert Falsum
. Und genau genommen ist eine abstrakte Maschine mit unvorhersehbarem Zustand nicht identisch mit "einem abgestürzten Computer".
In einer anweisungsorientierten Programmiersprache wird ein Codeblock als Liste von Anweisungen definiert. Mit anderen Worten, eine Anweisung ist eine Syntax, die Sie in einen Codeblock einfügen können, ohne einen Syntaxfehler zu verursachen.
Wikipedia definiert das Wort Aussage ähnlich
In der Computerprogrammierung ist eine Anweisung eine syntaktische Einheit einer imperativen Programmiersprache, die eine auszuführende Aktion ausdrückt. Ein in einer solchen Sprache geschriebenes Programm besteht aus einer Folge von einer oder mehreren Anweisungen
Beachten Sie die letztere Aussage. (obwohl "ein Programm" in diesem Fall technisch falsch ist, weil sowohl C als auch Java ein Programm ablehnen, das aus nichts von Anweisungen besteht.)
Wikipedia definiert den Wortausdruck als
Ein Ausdruck in einer Programmiersprache ist eine syntaktische Entität, die ausgewertet werden kann, um ihren Wert zu bestimmen
Dies ist jedoch falsch, da es sich bei Kotlin throw new Exception("")
um einen Ausdruck handelt, der jedoch bei der Auswertung einfach eine Ausnahme auslöst und niemals einen Wert zurückgibt.
In einer statisch typisierten Programmiersprache hat jeder Ausdruck einen Typ. Diese Definition funktioniert jedoch nicht in einer dynamisch typisierten Programmiersprache.
Persönlich definiere ich einen Ausdruck als eine Syntax, die mit einem Operator oder Funktionsaufrufen zusammengesetzt werden kann, um einen größeren Ausdruck zu erhalten. Dies ähnelt tatsächlich der Erklärung des Ausdrucks durch Wikipedia:
Es ist eine Kombination aus einer oder mehreren Konstanten, Variablen, Funktionen und Operatoren, die die Programmiersprache interpretiert (gemäß ihren besonderen Vorrang- und Assoziationsregeln) und berechnet, um in einer zustandsbehafteten Umgebung einen anderen Wert zu erzeugen ("zurückzugeben")
Das Problem liegt jedoch in der Programmiersprache C, wenn eine Funktion ausgeführt wird.
void executeSomething(void){
return;
}
Ist executeSomething()
ein Ausdruck oder eine Aussage? Nach meiner Definition handelt es sich um eine Aussage, da gemäß der Definition in der C-Referenzgrammatik von Microsoft
Sie können weder den (nicht vorhandenen) Wert eines Ausdrucks mit dem Typ void in irgendeiner Weise verwenden, noch können Sie einen void-Ausdruck (durch implizite oder explizite Konvertierung) in einen anderen Typ als void konvertieren
Dieselbe Seite zeigt jedoch deutlich, dass eine solche Syntax ein Ausdruck ist.
Um meine vorherige Antwort zu verbessern und zu validieren, sollten Definitionen von Begriffen der Programmiersprache gegebenenfalls aus der Theorie der Informatik erklärt werden.
Ein Ausdruck hat einen anderen Typ als den unteren Typ, dh er hat einen Wert. Eine Anweisung hat den Typ Unit oder Bottom.
Daraus folgt, dass eine Anweisung in einem Programm nur dann einen Effekt haben kann, wenn sie einen Nebeneffekt erzeugt, da sie entweder keinen Wert zurückgeben kann oder nur den Wert des Unit-Typs zurückgibt, der entweder nicht zuweisbar ist (in einigen Sprachen wie z Ein C void
) oder (wie in Scala) kann für eine verzögerte Auswertung der Aussage gespeichert werden.
Offensichtlich haben a @pragma
oder a /*comment*/
keinen Typ und unterscheiden sich somit von Aussagen. Somit wäre die einzige Art von Aussage, die keine Nebenwirkungen hätte, eine Nichtoperation. Nicht-Betrieb ist nur als Platzhalter für zukünftige Nebenwirkungen nützlich. Jede andere Handlung aufgrund einer Aussage wäre ein Nebeneffekt. Wiederum ist ein Compiler-Hinweis, z. B. @pragma
keine Anweisung, da er keinen Typ hat.
@pragma
oder /*comment*/
logisch inkonsistent.
Die meisten genau muss eine Aussage eine „Nebenwirkung“ (dh seinen Imperativ ) und ein Ausdruck muss hat einen Wert - Typen (dh nicht der Boden - Typen).
Der Typ einer Anweisung ist der Einheitentyp, aber aufgrund des Halting-Theorems ist die Einheit eine Fiktion, also sagen wir den unteren Typ .
Void
ist nicht genau der unterste Typ (es ist nicht der Untertyp aller möglichen Typen). Es existiert in Sprachen, die kein vollständig solides System haben . Das mag nach einer snobistischen Aussage klingen, aber Vollständigkeit wie Abweichungsanmerkungen sind für das Schreiben erweiterbarer Software von entscheidender Bedeutung.
Mal sehen, was Wikipedia zu diesem Thema zu sagen hat.
https://en.wikipedia.org/wiki/Statement_(computer_science)
In der Computerprogrammierung ist eine Anweisung das kleinste eigenständige Element einer imperativen Programmiersprache, das eine auszuführende Aktion ausdrückt .
Viele Sprachen (z. B. C) unterscheiden zwischen Anweisungen und Definitionen, wobei eine Anweisung nur ausführbaren Code enthält und eine Definition einen Bezeichner deklariert, während ein Ausdruck nur einen Wert ergibt.
pass
eine Anweisung. Es ist ein No-Op, und es bewertet nichts.