Ich möchte eine Frage zu mehreren Rückgabewerten stellen, warum dieses Konstrukt in Programmiersprachen (konzeptionelle und / oder technische Schwierigkeiten) nicht vorzuziehen ist. Ich habe etwas über Stapelrahmen gehört und wie sie Speicher für Rückgabewerte und variable Rückgabewerte reservieren, könnte dies problematisch machen, aber wenn jemand dies besser erklären könnte, wäre dies sehr dankbar.
In konktanetativen Sprachen (wie FORTH) ist es üblich und sehr nützlich, mehrere Rückgabewerte von einer Funktion zu haben, und ich stelle mir vor, dass so etwas in Java / C-ähnlichen Sprachen ebenfalls nützlich wäre (ein sehr einfaches Beispiel):
x, y = multRet(5);
multret(int x) {
return x+1;
return x+2;
exit;
}
Zur Verdeutlichung: Ich frage nicht, wie mehrere Werte zurückgegeben werden sollen (dies ist eine bekannte Frage mit bekannten Antworten), aber ich möchte eine Klarstellung darüber erhalten, warum diese Praxis in Programmiersprachen nicht üblich ist.
quelle
i=0; while (true) { return i; i+=1 }
?). Beachten Sie, dass einige funktionale Sprachen Tupel anbieten, eine bessere Alternative.return
Anweisung zurückgegeben wird. Wenn Sie Funktionen / Lambdas zurückgeben, haben Sie möglicherweise einen Punkt.Antworten:
Funktionen werden traditionell als Rückgabe eines einzelnen Wertes angesehen - in der Mathematik. Natürlich bilden mehrere Werte ein Tupel, aber in der Mathematik handelt es sich häufig um reelle Funktionen. Ich vermute, dass dieser Ursprung der Funktionen der Grund ist, warum Mehrfachrückgabewerte nicht so häufig sind. Das heißt, es ist einfach, sie auf verschiedene Arten zu emulieren (indem ein Tupel zurückgegeben wird oder indem mehrere Ausgabeparameter, dh solche, die als Referenz übergeben werden), und einige gängige moderne Sprachen (wie Python) unterstützen diese Konstruktion nativ.
quelle
Es ist vorzuziehen, mehrere Rückgabewerte zu haben . Ihnen zu fehlen ist schlechtes Design, schlicht und einfach. Wir sollten jedoch das Missverständnis beseitigen, dass so etwas sogar in dem Sinne existiert, an den Sie denken.
In Sprachen, die auf dem typisierten Lambda-Kalkül (den Vorläufer-Programmiersprachen) basieren, z. B. der ML-Familie Haskell, akzeptiert eine Funktion (Lambda-Abstraktion) nur ein einziges Argument und gibt ein einzelnes Ergebnis zurück (formeller ausgewertet). Wie können wir also eine Funktion erstellen, die mehrere Werte anzunehmen oder zurückzugeben scheint?
Die Antwort ist, dass wir Werte mithilfe von Produkttypen "zusammenkleben" können . Wenn A und B beide Typen sind, dann der ProdukttypA × B. enthält alle Tupel des Formulars ( a , b ) , wo a : A. und b : B. .
multiply
Um eine Funktion zu erstellen , die zwei ganze Zahlen miteinander multipliziert, geben wir ihr den Typmultiply: int * int -> int
. Es akzeptiert ein einzelnes Argument, das zufällig ein Tupel ist. Ebenso hätte eine Funktionsplit
, die eine Liste in zwei Teile aufteilt, den Typsplit: a list -> a list * a list
.Vielleicht war die Implementierung vor 30 Jahren ein berechtigtes Anliegen, eine solche Funktion aufzunehmen. Gemäß der C-Aufrufkonvention cedl (und es ist nur so, dass eine Konvention - kein Gott hat sie bestimmt) wird der Rückgabewert normalerweise in einem speziellen Register gespeichert
EAX
. Dies ist sicherlich extrem schnell zu lesen und zu schreiben, daher hat die Idee einige Vorteile.Aber es ist zu einschränkend, wie Sie betonen, und es gibt keinen Grund, warum wir diese Regel heutzutage befolgen müssen. Zum Beispiel könnten wir stattdessen die folgende Richtlinie verwenden: Verwenden Sie für eine kleine Anzahl von Rückgabewerten eine Reihe angegebener Register (wir haben jetzt mehr Register zur Verwendung als in den 80er Jahren). Bei größeren Daten können wir einen Zeiger auf eine Stelle im Speicher zurückgeben. Ich weiß nicht, was am besten ist. Ich bin kein Compiler-Autor. Die archaische C-Aufrufkonvention sollte jedoch keinen Einfluss auf die Semantik moderner Sprachen haben.
quelle
Syntax ist nicht erforderlich, wenn einfache Muster die Arbeit besser erledigen und Ihre vorgeschlagene Syntax für viele Menschen historisch verwirrend wäre. Dies bedeutet, dass Ihre Syntax schwer zu erlernen ist und zu Problemen führt, wenn Personen zwischen Ihrer Sprache und anderen wechseln.
Das einfache Muster, das dieses Problem löst, besteht darin, zu Beginn Ihrer Methode eine Variable mit dem Namen "returnValue" zu definieren und ihr dann Werte zuzuweisen. Wenn Sie mehrere Werte benötigen, verwenden Sie einfach eine Sammlung und fügen Sie stattdessen die Werte hinzu. Geben Sie dann diese Variable zurück. Problem gelöst.
Ihre Syntax würde bedeuten, dass Sie standardmäßig Sammlungen verarbeiten müssten. Dies ist frustrierend für Funktionen, die nur 1 Wert zurückgeben.
Und was ist, wenn jemand etwas falsch macht? Z.B.
oder
Soll der Compiler das fangen? Was ist, wenn Sie eine Schleife haben?
Bestenfalls wäre dies syntaktischer Zucker. Etwas, das keine neuen Funktionen hinzufügt, sondern nur aktuelle Muster komfortabler und kürzer im Code macht.
Im schlimmsten Fall würde dies zu einem schrecklichen, schrecklichen Codefehler führen. Nullzeigerausnahme in Massen.
quelle
Es ist hauptsächlich aus syntaktischen Gründen; In Ihrem eigenen Beispiel würde die erste return-Anweisung einen Wert und return zurückgeben und niemals die zweite return-Anweisung erreichen.
Heutzutage gibt es einige Sprachen, in denen ein Tupel zurückgegeben werden kann, dh null, ein oder mehrere Werte beliebigen Typs, die zu einem einzigen Wert zusammengefasst sind. Trotzdem geben sie einen zurück Wert zurück, nur einen etwas komplizierteren Wert. Normalerweise haben solche Sprachen auch die Möglichkeit, ein Tupel Null, einer oder mehreren übereinstimmenden Variablen zuzuweisen, häufig mit einer einfachen Möglichkeit, einige Elemente des Tupels zu ignorieren. Das könnte so aussehen:
Weisen Sie einem anderen Tupel ein Tupel zu:
Diese Syntax wäre nicht kompatibel mit C, wobei (x, y) ein Kommaausdruck in Klammern ist. Andererseits ist das Empfangen von zwei Werten viel einfacher als in C, wo Sie entweder die beiden Werte mit Zeigern übergeben oder eine Struktur für diesen Zweck deklarieren, sie füllen und zurückgeben. Bei Verwendung von Zeigern ist das dritte Beispiel, bei dem der Aufrufer nur an einem Wert interessiert ist, ein Schmerz. Daher ist diese Methode zum Zurückgeben von Tupeln sehr nützlich.
quelle
return x, y
und um die Rückgabewerte zu akzeptieren, die Sie tun würdenx,y = multiRet()