Was ist der Vorteil, wenn der Zuweisungsoperator einen Wert zurückgibt?

27

Ich entwickle eine Sprache, die sowohl Javascript als auch PHP ersetzen soll. (Ich sehe kein Problem damit. Es ist nicht so, dass eine dieser Sprachen eine große Installationsbasis hat.)

Eines der Dinge, die ich ändern wollte, war, den Zuweisungsoperator in einen Zuweisungsbefehl umzuwandeln und die Möglichkeit zu entfernen, den zurückgegebenen Wert zu verwenden.

x=1;          /* Assignment. */
if (x==1) {}  /* Comparison. */
x==1;         /* Error or warning, I've not decided yet. */
if (x=1) {}   /* Error. */

Ich weiß, dass dies bedeuten würde, dass diese einzeiligen Funktionen, die C-Leute so sehr lieben, nicht mehr funktionieren würden. Ich nahm an (mit wenigen Beweisen, die über meine persönlichen Erfahrungen hinausgehen), dass dies in den allermeisten Fällen wirklich als Vergleichsoperation gedacht war.

Oder ist es? Gibt es praktische Verwendungen des Rückgabewerts des Zuweisungsoperators, die nicht einfach umgeschrieben werden konnten? (Für jede Sprache, die ein solches Konzept hat.)

billpg
quelle
12
JS und PHP haben keine große "Installationsbasis"?
13.02.14 Uhr
47
@mri Ich vermute Sarkasmus.
Andy Hunt
12
Der einzige nützliche Fall, an den ich mich erinnern kann, ist while((x = getValue()) != null) {}. Ersetzungen sind hässlicher, da Sie breakdie x = getValueZuordnung entweder verwenden oder wiederholen müssen .
CodesInChaos
12
@mri Ooh nein, ich höre, dass diese beiden Sprachen nur triviale Dinge sind, ohne nennenswerte Investitionen. Sobald die wenigen Leute, die darauf bestehen, JS zu verwenden, meine Sprache sehen, werden sie zu meiner wechseln und müssen nie wieder schreiben. Ich bin mir ebenso sicher, dass die Browserhersteller sofort ein Update veröffentlichen werden, das neben JS auch meine Sprache enthält. :)
billpg
4
Ich würde Ihnen vorschlagen, dass, wenn Sie beabsichtigen, eine vorhandene Sprache zu verbessern, und Sie beabsichtigen, diese weitgehend zu übernehmen, eine hundertprozentige Abwärtskompatibilität mit der vorhandenen Sprache gut ist. Siehe TypeScript als Beispiel. Wenn Sie beabsichtigen, eine bessere Alternative zu einer vorhandenen Sprache anzubieten, haben Sie ein viel schwierigeres Problem. Eine neue Sprache muss ein existierendes realistisches Problem viel besser lösen als die existierende Sprache, um die Kosten für den Wechsel zu bezahlen. Eine Sprache zu lernen ist eine Investition , die sich auszahlen muss.
Eric Lippert

Antworten:

25

Technisch gesehen kann es sich lohnen, einige syntaktische Zucker beizubehalten, auch wenn sie trivial ersetzt werden können, wenn sie die Lesbarkeit einiger gängiger Operationen verbessern. Zuweisung als Ausdruck fällt jedoch nicht darunter. Die Gefahr, es anstelle eines Vergleichs zu tippen, bedeutet, dass es selten verwendet wird (manchmal sogar von Styleguides verboten) und bei jeder Verwendung eine doppelte Einstellung hervorruft. Mit anderen Worten, die Vorteile der Lesbarkeit sind in Anzahl und Größe gering.

Ein Blick auf bestehende Sprachen, die dies tun, kann sich lohnen.

  • Java und C # behalten die Zuweisung eines Ausdrucks bei, beseitigen jedoch die erwähnte Fallstricke, indem Bedingungen für die Auswertung zu Booleschen Werten erforderlich sind. Dies scheint meistens gut zu funktionieren, obwohl sich die Leute gelegentlich darüber beschweren, dass dies Zustände wie if (x)anstelle von if (x != null)oder if (x != 0)abhängig von der Art von nicht zulässt x.
  • Python macht die Zuweisung zu einer richtigen Anweisung anstelle eines Ausdrucks. Änderungsvorschläge erreichen gelegentlich die Mailingliste von python-ideas, aber mein subjektiver Eindruck ist, dass dies seltener vorkommt und jedes Mal weniger Rauschen erzeugt als andere "fehlende" Funktionen wie do-while-Schleifen, switch-Anweisungen, mehrzeilige Lambdas, etc.

Allerdings ermöglicht Python einen Sonderfall, auf einmal mehrere Namen zuweisen: a = b = c. Dies wird als äquivalent zu angesehen b = c; a = bund wird gelegentlich verwendet. Daher kann es sich lohnen, Ihre Sprache zu erweitern (aber ich würde es nicht schwitzen, da dieser Zusatz abwärtskompatibel sein sollte).


quelle
5
+1 für das Aufrufen, a = b = cwas die anderen Antworten nicht wirklich hervorbringen.
Leo
6
Eine dritte Lösung besteht darin, ein anderes Symbol für die Zuweisung zu verwenden. Pascal verwendet :=für die Zuordnung.
Brian
5
@ Brian: In der Tat, genauso wie C #. =ist Aufgabe, ==ist Vergleich.
Marjan Venema
3
In C # gibt so etwas wie if (a = true)eine C4706- Warnung aus ( The test value in a conditional expression was the result of an assignment.). GCC mit C wirft ebenfalls a warning: suggest parentheses around assignment used as truth value [-Wparentheses]. Diese Warnungen können mit einem zusätzlichen Satz von Klammern zum Schweigen gebracht werden. Sie sollen jedoch ausdrücklich darauf hinweisen, dass die Zuweisung beabsichtigt war.
Bob
2
@delnan Nur ein etwas allgemeiner Kommentar, aber er wurde ausgelöst durch "Entfernen der von Ihnen erwähnten Fallstricke, indem Bedingungen zum Auswerten in Booleschen Werten erforderlich sind" - a = true wird als Boolescher Wert ausgewertet und ist daher kein Fehler, sondern löst auch eine entsprechende Warnung in C # aus.
Bob
11

Gibt es praktische Verwendungen des Rückgabewerts des Zuweisungsoperators, die nicht einfach umgeschrieben werden konnten?

Generell nein. Die Vorstellung, dass der Wert eines Zuweisungsausdrucks der zugewiesene Wert ist, bedeutet, dass wir einen Ausdruck haben, der sowohl für seine Nebenwirkung als auch für seinen Wert verwendet werden kann und der von vielen als verwirrend angesehen wird.

Übliche Verwendungszwecke sind normalerweise, um Ausdrücke kompakt zu machen:

x = y = z;

hat die Semantik in C # von "konvertiere z in den Typ von y, weise den konvertierten Wert y zu, der konvertierte Wert ist der Wert des Ausdrucks, konvertiere den in den Typ von x, weise x zu".

Aber wir befinden uns bereits im Bereich der impertativen Nebenwirkungen in einem aussagekräftigen Kontext, so dass dies wirklich nur einen sehr geringen zwingenden Vorteil hat

y = z;
x = y;

Ähnlich ist es mit

M(x = 123);

eine Abkürzung für

x = 123;
M(x);

Wiederum verwenden wir im ursprünglichen Code einen Ausdruck sowohl für seine Nebenwirkungen als auch für seinen Wert und geben eine Aussage ab, die zwei Nebenwirkungen anstelle von einer hat. Beide stinken; Versuchen Sie, eine Nebenwirkung pro Anweisung zu erzielen, und verwenden Sie Ausdrücke für ihre Werte und nicht für ihre Nebenwirkungen.

Ich entwickle eine Sprache, die sowohl Javascript als auch PHP ersetzen soll.

Wenn Sie wirklich mutig sein und betonen möchten, dass Zuweisung eine Aussage und keine Gleichstellung ist, dann lautet mein Rat: Machen Sie es deutlich zu einer Zuweisungserklärung .

let x be 1;

Der Rote. Oder

x <-- 1;

oder noch besser:

1 --> x;

Oder noch besser

1 → x;

Es gibt absolut keine Möglichkeit, mit denen jemand verwechselt werden kann x == 1.

Eric Lippert
quelle
1
Ist die Welt bereit für Nicht-ASCII-Unicode-Symbole in Programmiersprachen?
billpg
So sehr ich es auch lieben würde, eines meiner Ziele ist es, dass das am besten geschriebene JavaScript ohne oder mit nur geringen Änderungen portiert werden kann.
billpg
2
@billpg: Ist die Welt bereit ? Ich weiß nicht - war die Welt 1964, Jahrzehnte vor der Erfindung von Unicode, bereit für APL? Hier ist ein Programm in APL, das eine zufällige Permutation von sechs Zahlen aus den ersten 40 x[⍋x←6?40] auswählt : APL benötigte eine eigene Spezialtastatur, aber es war eine ziemlich erfolgreiche Sprache.
Eric Lippert
@billpg: Macintosh Programmer's Workshop verwendete Nicht-ASCII-Symbole für Dinge wie Regex-Tags oder die Umleitung von stderr. Auf der anderen Seite hatte MPW den Vorteil, dass der Macintosh die Eingabe von Nicht-ASCII-Zeichen vereinfachte. Ich muss etwas Verwirrung darüber gestehen, warum der US-Tastaturtreiber keine anständigen Mittel zum Eingeben von Nicht-ASCII-Zeichen bereitstellt. Die Eingabe von Alt-Nummern erfordert nicht nur das Nachschlagen von Zeichencodes - in vielen Anwendungen funktioniert dies sogar nicht.
Supercat
Hm, warum würde man es vorziehen, "dem richtigen" zuzuweisen a+b*c --> x? Das kommt mir komisch vor.
Ruslan
9

Viele Sprachen wählen die Methode, mit der die Zuweisung zu einer Anweisung und nicht zu einem Ausdruck gemacht wird, einschließlich Python:

foo = 42 # works
if foo = 42: print "hi" # dies
bar(foo = 42) # keyword arg

und Golang:

var foo int
foo = 42 # works
if foo = 42 { fmt.Printn("hi") } # dies

Andere Sprachen haben keine Zuordnung, sondern Gültigkeitsbereichsbindungen, z. B. OCaml:

let foo = 42 in
  if foo = 42 then
    print_string "hi"

Ist letjedoch ein Ausdruck an sich.

Das Zulassen der Zuweisung hat den Vorteil, dass wir den Rückgabewert einer Funktion innerhalb der Bedingung direkt überprüfen können, z. B. in diesem Perl-Snippet:

if (my $result = some_computation()) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}

Perl überprüft die Deklaration zusätzlich nur auf diese Bedingung, was sie sehr nützlich macht. Es warnt auch, wenn Sie innerhalb einer Bedingung zuweisen, ohne dort eine neue Variable zu deklarieren - if ($foo = $bar)wird warnen, if (my $foo = $bar)wird nicht.

Die Zuordnung in einer anderen Anweisung ist in der Regel ausreichend, kann jedoch zu Problemen beim Scoping führen:

my $result = some_computation()
if ($result) {
  say "We succeeded, and the result is $result";
}
else {
  warn "Failed with $result";
}
# $result is still visible here - eek!

Golang verlässt sich stark auf Rückgabewerte für die Fehlerprüfung. Es erlaubt daher einer Bedingung, eine Initialisierungsanweisung zu treffen:

if result, err := some_computation(); err != nil {
  fmt.Printf("Failed with %d", result)
}
fmt.Printf("We succeeded, and the result is %d\n", result)

Andere Sprachen verwenden ein Typsystem, um nicht-boolesche Ausdrücke in einer Bedingung zu verbieten:

int foo;
if (foo = bar()) // Java does not like this

Dies schlägt natürlich fehl, wenn eine Funktion verwendet wird, die einen Booleschen Wert zurückgibt.

Wir haben jetzt verschiedene Mechanismen gesehen, um uns gegen unbeabsichtigte Zuweisungen zu verteidigen:

  • Zuweisung als Ausdruck nicht zulassen
  • Verwenden Sie die statische Typprüfung
  • Zuordnung existiert nicht, wir haben nur letBindungen
  • Erlaube eine Initialisierungsanweisung, lass die Zuordnung sonst nicht zu
  • Zuweisung innerhalb einer Bedingung ohne Deklaration nicht zulassen

Ich habe sie nach aufsteigenden Präferenzen geordnet - Zuweisungen in Ausdrücken können nützlich sein (und es ist einfach, Pythons Probleme zu umgehen, indem Sie eine explizite Deklarationssyntax und eine andere benannte Argument-Syntax verwenden). Aber es ist in Ordnung, sie zu verbieten, da es viele andere Optionen gibt, die den gleichen Effekt haben.

Fehlerfreier Code ist wichtiger als knapper Code.

amon
quelle
+1 für "Zuweisung als Ausdruck nicht zulassen". Die Anwendungsfälle für die Zuweisung als Ausdruck rechtfertigen nicht das Potenzial für Fehler und Lesbarkeitsprobleme.
poke
7

Sie sagten: "Ich dachte mir (mit wenigen Beweisen, die über meine persönlichen Erfahrungen hinausgehen), dass dies in den allermeisten Fällen wirklich als Vergleichsoperation gedacht war."

Warum nicht das Problem beheben?

Verwenden Sie anstelle von = für die Zuweisung und == für die Gleichheitsprüfung: = für die Zuweisung und = (oder sogar ==) für die Gleichheit.

Beobachten:

if (a=foo(bar)) {}  // obviously equality
if (a := foo(bar)) { do something with a } // obviously assignment

Wenn Sie es dem Programmierer erschweren möchten, Zuweisung mit Gleichheit zu verwechseln, dann erschweren Sie es.

Zur gleichen Zeit, wenn Sie das Problem WIRKLICH beheben wollten, würden Sie den C-Topf entfernen, der behauptete, Boolesche Werte seien nur Ganzzahlen mit vordefinierten symbolischen Zuckernamen. Bilden Sie sie eine andere Art zusammen. Dann, anstatt zu sagen

int a = some_value();
if (a) {}

Sie zwingen den Programmierer zu schreiben:

int a = some_value();
if (a /= 0) {} // Note that /= means 'not equal'.  This is your Ada lesson for today.

Tatsache ist, dass die Zuweisung als Operator ein sehr nützliches Konstrukt ist. Wir haben die Rasierklingen nicht eliminiert, weil sich einige Leute selbst schneiden. Stattdessen erfand König Gillette den Sicherheitsrasierer.

John R. Strohm
quelle
2
(1) :=für die Zuweisung und =für die Gleichheit könnte dieses Problem beheben, aber auf Kosten der Entfremdung jedes Programmierers, der nicht mit einem kleinen Satz von Nicht-Mainstream-Sprachen aufgewachsen ist. (2) Andere Typen als bools, die unter Bedingungen zulässig sind, sind nicht immer auf das Verwechseln von bools und ganzen Zahlen zurückzuführen. Es reicht aus, anderen Typen eine wahre / falsche Interpretation zu geben. Neuere Sprachen, die keine Angst haben, von C abzuweichen, haben dies für viele andere Typen als Ganzzahlen getan (z. B. betrachtet Python leere Sammlungen als falsch).
1
Und zu Rasierklingen: Diese dienen einem Anwendungsfall, der Schärfe erfordert. Andererseits bin ich nicht davon überzeugt, dass eine gute Programmierung die Zuweisung von Variablen während einer Ausdrucksbewertung erfordert. Wenn es eine einfache, einfache, sichere und kostengünstige Möglichkeit gäbe, Körperhaare ohne scharfe Kanten verschwinden zu lassen, wären die Rasierklingen sicherlich verschoben oder zumindest viel seltener geworden.
1
@delnan: Ein weiser Mann sagte einmal "Mach es so einfach wie möglich, aber nicht einfacher." Wenn Sie die große Mehrheit der a = b vs. a == b-Fehler beseitigen möchten, können Sie die Domäne der bedingten Tests auf Boolesche Werte beschränken und die Standardtypkonvertierungsregeln für <other> -> Boolesche Werte eliminieren Dort. Zu diesem Zeitpunkt ist if (a = b) {} nur syntaktisch zulässig, wenn a und b beide boolesch sind und a ein zulässiger Wert ist.
John R. Strohm
Eine Anweisung zuzuweisen ist mindestens so einfach wie - wahrscheinlich sogar einfacher als - die Änderungen, die Sie vorschlagen, und erreicht mindestens so viel - wahrscheinlich sogar mehr (lässt nicht einmal if (a = b)lvalue a, boolean a, b zu). In einer Sprache ohne statische Eingabe gibt es auch viel bessere Fehlermeldungen (zur Analysezeit vs. Laufzeit). Außerdem ist die Vermeidung von "a = b vs. a == b-Fehlern" möglicherweise nicht das einzige relevante Ziel. Zum Beispiel möchte ich auch zulassen, dass Code if items:bedeutet if len(items) != 0und dass ich aufgeben muss, um Bedingungen auf Boolesche Werte zu beschränken.
1
@delnan Pascal ist eine Nicht-Mainstream-Sprache? Millionen von Menschen lernten das Programmieren mit Pascal (und / oder Modula, das von Pascal abgeleitet ist). Und Delphi wird immer noch häufig in vielen Ländern verwendet (vielleicht nicht so häufig in Ihren).
14.
5

Um die Frage tatsächlich zu beantworten: Ja, es gibt zahlreiche Verwendungszwecke, auch wenn sie geringfügig in der Nische liegen.

Zum Beispiel in Java:

while ((Object ob = x.next()) != null) {
    // This will loop through calling next() until it returns null
    // The value of the returned object is available as ob within the loop
}

Die Alternative ohne Verwendung der eingebetteten Zuweisung erfordert die obaußerhalb des Gültigkeitsbereichs der Schleife definierte und zwei separate Code-Speicherorte, die x.next () aufrufen.

Es wurde bereits erwähnt, dass Sie mehrere Variablen in einem Schritt zuweisen können.

x = y = z = 3;

Diese Art von Dingen wird am häufigsten verwendet, aber kreative Programmierer lassen sich immer mehr einfallen.

Tim B
quelle
Würde die while-Schleifenbedingung obbei jeder Schleife die Zuordnung aufheben und ein neues Objekt erstellen ?
user3932000
@ user3932000 In diesem Fall wahrscheinlich nicht, normalerweise iteriert x.next () über etwas. Es ist jedoch durchaus möglich, dass dies der Fall ist.
Tim B
1

Da Sie alle Regeln aufstellen müssen, warum sollten Sie jetzt zulassen, dass die Zuweisung einen Wert ändert, und Zuweisungen in bedingten Schritten einfach nicht zulassen? Dies gibt Ihnen den syntaktischen Zucker, um die Initialisierung zu vereinfachen und gleichzeitig einen häufigen Codierungsfehler zu vermeiden.

Mit anderen Worten, machen Sie dies legal:

a=b=c=0;

Aber mach das illegal:

if (a=b) ...
Bryan Oakley
quelle
2
Das scheint eher eine Ad-hoc-Regel zu sein. Zuweisung zu einer Anweisung zu machen und sie zu erweitern, a = b = cscheint orthogonaler und auch einfacher zu implementieren zu sein. Diese beiden Herangehensweisen sind sich nicht einig über die Zuweisung in expressions ( a + (b = c)), aber Sie haben keine Partei ergriffen, daher nehme ich an, dass sie keine Rolle spielen.
"Einfach zu implementieren" sollte keine große Rolle spielen. Sie definieren eine Benutzeroberfläche - die Bedürfnisse der Benutzer stehen an erster Stelle. Sie müssen sich nur fragen, ob dieses Verhalten dem Benutzer hilft oder ihn behindert.
Bryan Oakley
Wenn Sie die implizite Konvertierung nach Bool nicht zulassen, müssen Sie sich keine Gedanken über die Zuweisung von Bedingungen machen
Ratschenfreak
Einfacher umzusetzen war nur eines meiner Argumente. Was ist mit dem Rest? Aus der Sicht der Benutzeroberfläche möchte ich hinzufügen, dass inkohärentes Design und Ad-hoc-Ausnahmen den Benutzer im Allgemeinen daran hindern, die Regeln zu ärgern und zu verinnerlichen.
@ratchetfreak Sie könnten immer noch ein Problem mit der Zuweisung von tatsächlichen Bools haben
jk.
0

Durch die Klänge davon sind Sie auf dem Weg, eine ziemlich strenge Sprache zu schaffen.

In diesem Sinne zwingen Sie die Leute zum Schreiben:

a=c;
b=c;

anstatt:

a=b=c;

scheint eine Verbesserung zu sein, die Menschen davon abhält, Folgendes zu tun:

if (a=b) {

als sie vorhatten zu tun:

if (a==b) {

Letztendlich lassen sich solche Fehler jedoch leicht erkennen und warnen, ob es sich um rechtliche Vorschriften handelt oder nicht.

Es gibt jedoch Situationen, in denen Folgendes ausgeführt wird:

a=c;
b=c;

bedeutet das nicht

if (a==b) {

wird wahr sein.

Wenn c tatsächlich eine Funktion c () ist, kann sie bei jedem Aufruf unterschiedliche Ergebnisse liefern. (Es könnte auch rechenintensiv sein ...)

Ebenso, wenn c ein Zeiger auf speicherabgebildete Hardware ist, dann

a=*c;
b=*c;

sind beide wahrscheinlich unterschiedlich und können bei jedem Lesevorgang auch elektronische Auswirkungen auf die Hardware haben.

Es gibt viele andere Permutationen mit Hardware, bei denen Sie genau wissen müssen, aus welchen Speicheradressen gelesen, in welche geschrieben und unter bestimmten zeitlichen Einschränkungen mehrere Zuweisungen auf derselben Leitung ausgeführt werden, und dies schnell, einfach und offensichtlich, ohne das damit verbundene Timing-Risiko temporäre Variablen einführen

Michael Shaw
quelle
4
Das entspricht a = b = cnicht a = c; b = c, es ist b = c; a = b. Dies vermeidet die Wiederholung von Nebenwirkungen und behält auch die Modifikation von aund bin derselben Reihenfolge bei. Außerdem sind all diese hardwarebezogenen Argumente ziemlich dumm: Die meisten Sprachen sind keine Systemsprachen und wurden weder zur Lösung dieser Probleme entwickelt, noch werden sie in Situationen verwendet, in denen diese Probleme auftreten. Dies gilt in zweifacher Hinsicht für eine Sprache, die versucht, JavaScript und / oder PHP zu verdrängen.
Delnan, das Problem war nicht, dass diese erfundenen Beispiele es sind. Der Punkt besteht immer noch darin, dass sie die Arten von Stellen zeigen, an denen das Schreiben von a = b = c üblich ist, und im Hardwarefall als bewährte Vorgehensweise gelten, wie es das OP gefordert hat. Ich bin sicher, dass sie in der Lage sein werden, ihre Relevanz für die erwartete Umgebung zu berücksichtigen
Michael Shaw,
Wenn ich zurückblicke, ist mein Problem mit dieser Antwort nicht in erster Linie, dass sie sich auf Anwendungsfälle der Systemprogrammierung konzentriert (obwohl das schon schlimm genug wäre, so wie es geschrieben wurde), sondern dass es auf der Annahme einer falschen Umschreibung beruht. Die Beispiele sind keine Beispiele für Orte, an denen a=b=chäufig / nützlich ist, sondern Beispiele für Orte, an denen Reihenfolge und Anzahl der Nebenwirkungen berücksichtigt werden müssen. Das ist völlig unabhängig. Schreiben Sie die verkettete Zuordnung korrekt um und beide Varianten sind gleichermaßen korrekt.
@delnan: Der r-Wert wird in den Typ von bin einem Temp konvertiert und dieser wird in den Typ von ain einem anderen Temp konvertiert . Der relative Zeitpunkt, zu dem diese Werte tatsächlich gespeichert werden, ist nicht angegeben. Aus Sicht des Sprachdesigns halte ich es für sinnvoll, zu verlangen, dass alle l-Werte in einer Mehrfachzuweisungsanweisung den gleichen Typ haben, und möglicherweise auch, dass keiner von ihnen flüchtig ist.
Supercat
0

Der größte Vorteil für mich, wenn die Zuweisung ein Ausdruck ist, ist, dass Ihre Grammatik einfacher wird, wenn eines Ihrer Ziele darin besteht, dass "alles ein Ausdruck ist" - insbesondere ein Ziel von LISP.

Python hat das nicht; es hat Ausdrücke und Anweisungen, wobei die Zuweisung eine Anweisung ist. Da Python ein lambdaFormular jedoch als einen einzigen parametrisierten Ausdruck definiert , können Sie innerhalb eines Lambda keine Variablen zuweisen. Dies ist manchmal unpraktisch, aber kein kritisches Problem, und es ist der einzige Nachteil meiner Erfahrung, dass die Zuweisung eine Aussage in Python ist.

Eine Möglichkeit, die Zuweisung bzw. die Auswirkung der Zuweisung als Ausdruck zuzulassen, ohne die if(x=1)Unfallgefahr von C einzuführen , besteht darin, ein LISP-ähnliches letKonstrukt zu verwenden, wie (let ((x 2) (y 3)) (+ x y))es in Ihrer Sprache möglicherweise als ausgewertet wird 5. Die Verwendung letdieser Methode muss in Ihrer Sprache technisch gesehen überhaupt nicht zugeordnet werden, wenn Sie leteinen lexikalischen Bereich definieren. Auf diese Weise definiert, letkönnte ein Konstrukt auf dieselbe Weise kompiliert werden wie das Konstruieren und Aufrufen einer verschachtelten Abschlussfunktion mit Argumenten.

Auf der anderen Seite, wenn Sie sich nur mit dem if(x=1)Fall befassen , aber möchten, dass die Zuweisung ein Ausdruck wie in C ist, reicht es vielleicht aus, nur verschiedene Token zu wählen. Zuordnung: x := 1oder x <- 1. Vergleich: x == 1. Syntaxfehler: x = 1.

wberry
quelle
1
letunterscheidet sich von der Zuweisung in mehr als der technischen Einführung einer neuen Variablen in einem neuen Bereich. Für den Anfang hat es keine Auswirkung auf den Code außerhalb des letKörpers des Benutzers und erfordert daher, den gesamten Code (was die Variable verwenden sollte) weiter zu verschachteln, ein wesentlicher Nachteil in zuweisungsintensivem Code. Wenn man diesen Weg beschreiten set!würde , wäre dies das bessere Lisp-Analogon - ganz anders als im Vergleich, aber ohne Verschachtelung oder ein neues Oszilloskop.
@delnan: Ich würde gerne eine Deklarations- und Zuweisungssyntax sehen, die eine Neuzuweisung verbietet, aber eine Neudeklaration zulässt, vorausgesetzt, dass (1) eine Neudeklaration nur für Deklarations- und Zuweisungskennungen zulässig ist, und (2 ) Neudeklaration würde eine Variable in allen umschließenden Bereichen "deklarieren". Somit wäre der Wert eines gültigen Bezeichners der Wert, der in der vorherigen Deklaration dieses Namens zugewiesen wurde. Das scheint ein bisschen netter zu sein, als Scoping-Blöcke für Variablen hinzuzufügen, die nur für wenige Zeilen verwendet werden, oder neue Namen für jede temporäre Variable zu formulieren.
Supercat
0

Ich weiß, dass dies bedeuten würde, dass diese einzeiligen Funktionen, die C-Leute so sehr lieben, nicht mehr funktionieren würden. Ich nahm an (mit wenigen Beweisen, die über meine persönlichen Erfahrungen hinausgehen), dass dies in den allermeisten Fällen wirklich als Vergleichsoperation gedacht war.

Tatsächlich. Dies ist nichts Neues, alle sicheren Teilmengen der C-Sprache haben bereits diese Schlussfolgerung gezogen.

MISRA-C, CERT-C und so weiter verbieten Zuteilung innerhalb von Bedingungen, einfach weil es gefährlich ist.

Es gibt keinen Fall, in dem Code, der sich auf die Zuweisung innerhalb von Bedingungen stützt, nicht umgeschrieben werden kann.


Darüber hinaus warnen solche Standards auch davor, Code zu schreiben, der auf der Reihenfolge der Bewertung beruht. Ein x=y=z;solcher Fall ist die Mehrfachzuweisung in einer einzelnen Zeile . Wenn eine Zeile mit mehreren Zuweisungen Nebenwirkungen enthält (Aufrufen von Funktionen, Zugreifen auf flüchtige Variablen usw.), können Sie nicht wissen, welche Nebenwirkungen zuerst auftreten.

Zwischen der Auswertung der Operanden liegen keine Sequenzpunkte. Daher können wir nicht wissen, ob der Unterausdruck yvorher oder nachher ausgewertet wird z: Es handelt sich um ein nicht angegebenes Verhalten in C. Daher ist ein solcher Code möglicherweise unzuverlässig, nicht portierbar und nicht konform mit den genannten sicheren Untermengen von C.

Die Lösung wäre gewesen, den Code durch zu ersetzen y=z; x=y;. Dies fügt einen Sequenzpunkt hinzu und garantiert die Reihenfolge der Auswertung.


Ausgehend von all den Problemen, die dies in C verursachte, würde jede moderne Sprache sowohl die Zuweisung innerhalb von Bedingungen als auch die Mehrfachzuweisung in einer einzelnen Zeile verbessern.


quelle