it-swarm.com.de

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

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 wenig Beweisen, die über meine persönlichen Erfahrungen hinausgingen), dass dies in den allermeisten Fällen eigentlich eine Vergleichsoperation sein sollte.

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

27
billpg

Technisch gesehen kann es sich lohnen, etwas syntaktischen Zucker aufzubewahren, auch wenn er trivial ersetzt werden kann, wenn er die Lesbarkeit einer gängigen Operation verbessert. Aber die Zuweisung als Ausdruck fällt 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 Lesbarkeitsvorteile sind in Anzahl und Größe gering.

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

  • Java und C # behalten die Zuweisung als Ausdruck bei, beseitigen jedoch die von Ihnen erwähnte Gefahr, indem Bedingungen für die Auswertung nach Booleschen Werten erforderlich sind. Dies scheint meistens gut zu funktionieren, obwohl sich die Leute gelegentlich darüber beschweren, dass dies Bedingungen wie if (x) anstelle von if (x != null) oder if (x != 0) je nach Art von x.
  • Python macht die Zuweisung zu einer richtigen Anweisung anstelle eines Ausdrucks. Vorschläge, dies zu ändern, erreichen gelegentlich die Python-Ideen-Mailingliste, 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, usw.

Python erlaubt jedoch einen Sonderfall, der mehreren Namen gleichzeitig zugewiesen wird: a = b = c. Dies wird als eine Anweisung angesehen, die b = c; a = b Entspricht, und wird gelegentlich verwendet. Daher kann es sich auch lohnen, Ihre Sprache zu erweitern (aber ich würde es nicht schwitzen, da diese Ergänzung abwärtskompatibel sein sollte).

25
user7043

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

Im Allgemeinen nein. Die Idee, 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 verwendet werden kann sein Wert , und das wird von vielen als verwirrend angesehen.

Übliche Verwendungen 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 diesen in den Typ von x, ordne x zu".

Aber wir befinden uns bereits im Bereich der impertativen Nebenwirkungen in einem Statement-Kontext, daher hat dies wirklich nur einen sehr geringen zwingenden Nutzen

y = z;
x = y;

Ähnliches gilt für

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, 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 Gleichheit ist, dann ist mein Rat: machen Sie es klar 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, diese mit x == 1 Zu verwechseln.

11
Eric Lippert

Viele Sprachen wählen den Weg, um die Zuweisung eher zu einer Anweisung als zu einem Ausdruck zu machen, 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 Bindungen mit Gültigkeitsbereich, z. OCaml:

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

let ist jedoch selbst ein Ausdruck.

Der Vorteil des Zulassens einer Zuweisung besteht darin, dass wir den Rückgabewert einer Funktion innerhalb der Bedingung, z. in diesem Perl-Snippet:

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

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

Die Zuweisung in einer anderen Anweisung ist normalerweise ausreichend, kann jedoch zu Problemen mit dem Umfang 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 ist bei der Fehlerprüfung stark auf Rückgabewerte angewiesen. Es erlaubt daher einer Bedingung, eine Initialisierungsanweisung zu erstellen:

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 innerhalb einer Bedingung nicht zuzulassen:

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 versehentliche Zuordnung zu verteidigen:

  • Zuweisung als Ausdruck nicht zulassen
  • Verwenden Sie die statische Typprüfung
  • Zuordnung existiert nicht, wir haben nur let Bindungen
  • Erlauben Sie eine Initialisierungsanweisung, andernfalls verbieten Sie die Zuweisung
  • Zuweisung innerhalb einer Bedingung ohne Deklaration nicht zulassen

Ich habe sie nach aufsteigender Präferenz geordnet - Zuweisungen in Ausdrücken können nützlich sein (und es ist einfach, Pythons Probleme zu umgehen, indem eine explizite Deklarationssyntax und eine andere benannte Argumentensyntax verwendet werden). Es ist jedoch in Ordnung, sie nicht zuzulassen, da es viele andere Optionen gibt, die den gleichen Effekt haben.

Fehlerfreier Code ist wichtiger als knapper Code.

9
amon

Sie sagten: "Ich habe mir gedacht (mit wenig 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 den Gleichheitstest: = für die Zuweisung und = (oder sogar ==) für die Gleichstellung.

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, die Zuweisung mit Gleichheit zu verwechseln, machen Sie es schwieriger.

Wenn Sie das Problem WIRKLICH beheben möchten, entfernen Sie gleichzeitig den C-Topf, der behauptet, Boolesche Werte seien nur Ganzzahlen mit vordefinierten symbolischen Zuckernamen. Machen Sie sie zu einem ganz anderen Typ. 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 Rasierklingen nicht beseitigt, weil sich einige Leute geschnitten haben. Stattdessen erfand König Gillette den Rasierapparat.

7
John R. Strohm

Um die Frage tatsächlich zu beantworten: Ja, es gibt zahlreiche Verwendungszwecke, obwohl sie eine kleine Nische darstellen.

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 das außerhalb des Bereichs der Schleife definierte ob und zwei separate Codepositionen, die x.next () aufrufen.

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

x = y = z = 3;

Diese Art von Dingen wird am häufigsten verwendet, aber kreative Programmierer werden immer mehr finden.

5
Tim B

Da Sie alle Regeln zusammenstellen können, warum sollten Sie jetzt zulassen, dass die Zuweisung einen Wert ändert, und einfach nicht Zuweisungen innerhalb bedingter Schritte zulassen? Dies gibt Ihnen den syntaktischen Zucker, um Initialisierungen 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) ...
1
Bryan Oakley

Der größte Vorteil für mich, wenn Zuweisung ein Ausdruck ist, besteht darin, 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 definiert ein lambda-Formular als ein einzelnes parametrisiertes Ausdruck definiert, bedeutet dies, dass Sie keine Variablen innerhalb eines Lambda zuweisen können. Dies ist unpraktisch Manchmal, aber kein kritisches Problem, und es ist der einzige Nachteil meiner Erfahrung, wenn die Zuweisung eine Aussage in Python ist.

Eine Möglichkeit, die Zuweisung oder vielmehr den Effekt der Zuweisung als Ausdruck zuzulassen, ohne das Potenzial für if(x=1) Unfälle in C einzuführen, besteht darin, ein LISP-ähnliches let -Konstrukt zu verwenden, z (let ((x 2) (y 3)) (+ x y)), Der in Ihrer Sprache möglicherweise als 5 Bewertet wird. Die Verwendung von let auf diese Weise muss technisch gesehen überhaupt keine Zuordnung in Ihrer Sprache sein, wenn Sie let als Erstellen eines lexikalischen Bereichs definieren. Auf diese Weise definiert, kann ein let -Konstrukt genauso kompiliert werden wie das Erstellen und Aufrufen einer verschachtelten Abschlussfunktion mit Argumenten.

Wenn Sie sich jedoch nur mit dem Fall if(x=1) befassen, aber möchten, dass die Zuweisung ein Ausdruck wie in C ist, reicht es möglicherweise aus, nur verschiedene Token auszuwählen. Zuordnung: x := 1 Oder x <- 1. Vergleich: x == 1. Syntaxfehler: x = 1.

0
wberry

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 wenig Beweisen, die über meine persönlichen Erfahrungen hinausgingen), dass dies in den allermeisten Fällen eigentlich eine Vergleichsoperation sein sollte.

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

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

Es gibt keinen Fall, in dem Code, der auf der Zuweisung innerhalb von Bedingungen beruht, nicht neu geschrieben werden kann.


Darüber hinaus warnen solche Standards auch davor, Code zu schreiben, der auf der Reihenfolge der Bewertung beruht. Ein solcher Fall sind mehrere Zuweisungen in einer einzelnen Zeile x=y=z;. 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 werden.

Es gibt keine Sequenzpunkte zwischen der Auswertung der Operanden. Wir können also nicht wissen, ob der Unterausdruck y vor oder nach z ausgewertet wird: Es handelt sich um ein nicht spezifiziertes Verhalten in C. Daher ist ein solcher Code möglicherweise unzuverlässig, nicht portierbar und entspricht nicht dem genannten Safe Teilmengen von C.

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


Basierend auf all den Problemen, die dies in C verursacht hat, würde jede moderne Sprache sowohl die Zuweisung innerhalb von Bedingungen als auch mehrere Zuweisungen in einer einzelnen Zeile gut verbieten.

0
user29079

Mit den Klängen sind Sie auf dem Weg, eine ziemlich strenge Sprache zu schaffen.

In diesem Sinne zwingen die Leute zum Schreiben:

a=c;
b=c;

anstatt:

a=b=c;

könnte eine Verbesserung sein, um Menschen daran zu hindern:

if (a=b) {

als sie vorhatten zu tun:

if (a==b) {

letztendlich sind diese Fehler jedoch leicht zu erkennen und warnen davor, ob es sich um gesetzliche Vorschriften handelt oder nicht.

Es gibt jedoch Situationen, in denen:

a=c;
b=c;

heißt das nicht

if (a==b) {

wird wahr sein.

Wenn c tatsächlich eine Funktion ist c()), kann es bei jedem Aufruf unterschiedliche Ergebnisse zurückgeben (dies kann auch rechenintensiv sein ...)

Wenn c ein Zeiger auf speicherabgebildete Hardware ist, dann ebenfalls

a=*c;
b=*c;

sind wahrscheinlich beide 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 angeben müssen, aus welchen Speicheradressen gelesen, in und unter bestimmten Zeiteinschränkungen geschrieben werden soll, wobei mehrere Zuweisungen in derselben Zeile schnell, einfach und offensichtlich sind, ohne dass das Zeitrisiko besteht temporäre Variablen einführen

0
Michael Shaw