Gibt es einen Grund, warum Funktionen in den meisten (?) Programmiersprachen so konzipiert sind, dass sie eine beliebige Anzahl von Eingabeparametern unterstützen, jedoch nur einen Rückgabewert?
In den meisten Sprachen ist es möglich, diese Einschränkung zu umgehen, z. B. durch die Verwendung von out-Parametern, die Rückgabe von Zeigern oder die Definition / Rückgabe von Strukturen / Klassen. Es ist jedoch merkwürdig, dass Programmiersprachen nicht so konzipiert wurden, dass sie mehrere Rückgabewerte auf "natürlichere" Weise unterstützen.
Gibt es eine Erklärung dafür?
def f(): return (1,2,3)
und dann können Sie verwenden Tupel-Auspacken auf „Split“ das Tupel:a,b,c = f() #a=1,b=2,c=3
. Keine Notwendigkeit, ein Array zu erstellen und Elemente manuell zu extrahieren, keine Notwendigkeit, eine neue Klasse zu definieren.[a, b] = f()
vs.[a, b, c] = f()
) bestimmt und innerhalbf
von ermitteltnargout
. Ich bin kein großer Fan von Matlab, aber das ist manchmal recht praktisch.Antworten:
Einige Sprachen wie Python unterstützen mehrere Rückgabewerte nativ, während einige Sprachen wie C # sie über ihre Basisbibliotheken unterstützen.
Aber im Allgemeinen werden selbst in Sprachen, die sie unterstützen, mehrere Rückgabewerte nicht oft verwendet, weil sie schlampig sind:
Es ist leicht, die Reihenfolge der Rückgabewerte zu verwechseln
(Aus dem gleichen Grund vermeiden es viele Leute, zu viele Parameter für eine Funktion zu haben. Einige gehen sogar davon aus, dass eine Funktion niemals zwei Parameter desselben Typs haben sollte!)
Sie sind stärker typisiert, sie halten die Rückgabewerte als eine logische Einheit gruppiert und sie halten die Namen (Eigenschaften) der Rückgabewerte über alle Verwendungen hinweg konsistent.
Der eine Ort, an dem sie sehr praktisch sind, sind Sprachen (wie Python), in denen mehrere Rückgabewerte von einer Funktion als mehrere Eingabeparameter für eine andere verwendet werden können. Aber die Anwendungsfälle, in denen dies ein besseres Design als die Verwendung einer Klasse ist, sind ziemlich schlank.
quelle
()
. Ist das eine Sache oder nichts? Persönlich würde ich sagen, es ist eine Sache. Ich kannx = ()
ganz gut zuweisen , genau wie ich zuweisen kannx = randomTuple()
. Wenn das zurückgegebene Tupel leer ist oder nicht, kann ich das zurückgegebene Tupel trotzdem zuweisenx
.Weil Funktionen mathematische Konstrukte sind, die eine Berechnung durchführen und ein Ergebnis zurückgeben. Tatsächlich konzentriert sich vieles, was "unter der Haube" nicht weniger Programmiersprachen liegt, nur auf eine Eingabe und eine Ausgabe, wobei mehrere Eingaben nur eine dünne Umhüllung der Eingabe darstellen - und wenn eine einzelne Wertausgabe mit einer einzigen nicht funktioniert Kohäsive Struktur (oder Tupel oder
Maybe
) ist die Ausgabe (obwohl dieser "einzelne" Rückgabewert aus vielen Werten besteht).Dies hat sich nicht geändert, da Programmierer herausgefunden haben, dass
out
Parameter umständliche Konstrukte sind, die nur in einer begrenzten Anzahl von Szenarien nützlich sind. Wie bei vielen anderen Dingen ist der Support nicht da, weil der Bedarf / die Nachfrage nicht da ist.quelle
In der Mathematik ist eine "wohldefinierte" Funktion eine Funktion, bei der es für eine bestimmte Eingabe nur 1 Ausgabe gibt (als Randnotiz können Sie nur einzelne Eingabefunktionen haben und semantisch immer noch mehrere Eingaben mithilfe von currying erhalten ).
Für mehrwertige Funktionen (z. B. Quadratwurzel einer positiven Ganzzahl) ist es ausreichend, eine Sammlung oder eine Folge von Werten zurückzugeben.
Für die Arten von Funktionen, über die Sie sprechen (dh Funktionen, die mehrere Werte verschiedener Typen zurückgeben ), sehe ich das etwas anders als Sie scheinen: Ich sehe die Notwendigkeit / Verwendung von out-Parametern als eine Problemumgehung für ein besseres Design oder eine nützlichere Datenstruktur. Ich würde es zum Beispiel vorziehen, wenn
*.TryParse(...)
Methoden eineMaybe<T>
Monade zurückgeben würden, anstatt einen out-Parameter zu verwenden. Denken Sie an diesen Code in F #:Die Unterstützung für Compiler / IDE / Analyse ist für diese Konstrukte sehr gut. Dies würde einen Großteil des "Bedarfs" für unsere Parameter lösen. Um ganz ehrlich zu sein, ich kann mir keine andere Methode vorstellen, bei der dies nicht die Lösung wäre.
Für andere Szenarien, an die ich mich nicht erinnern kann, reicht ein einfaches Tupel aus.
quelle
var (value, success) = ParseInt("foo");
würde zur Kompilierungszeit überprüft, da(int, bool) ParseInt(string s) { }
deklariert wurde. Ich weiß, dass dies mit Generika gemacht werden kann, aber es wäre trotzdem eine schöne Sprachergänzung.Zusätzlich zu dem, was bereits gesagt wurde, wenn Sie sich die in der Assembly verwendeten Paradigmen ansehen, wenn eine Funktion zurückgibt, hinterlässt sie einen Zeiger auf das zurückgegebene Objekt in einem bestimmten Register. Wenn sie Variablen- / Mehrfachregister verwenden, würde die aufrufende Funktion nicht wissen, wo sie die zurückgegebenen Werte erhält, wenn sich diese Funktion in einer Bibliothek befindet. Dies würde das Verknüpfen mit Bibliotheken erschweren und anstatt eine willkürliche Anzahl von wiederverwendbaren Zeigern festzulegen, würden sie mit einem Zeiger verknüpft. Höhere Sprachen haben nicht die gleiche Entschuldigung.
quelle
Viele Anwendungsfälle, in denen Sie in der Vergangenheit mehrere Rückgabewerte verwendet hätten, sind mit modernen Sprachfunktionen nicht mehr erforderlich. Möchten Sie einen Fehlercode zurückgeben? Wirf eine Ausnahme oder gib eine zurück
Either<T, Throwable>
. Möchten Sie ein optionales Ergebnis zurückgeben? Rückgabe anOption<T>
. Möchten Sie einen von mehreren Typen zurückgeben? Geben Sie eineEither<T1, T2>
oder eine markierte Union zurück.Und selbst in den Fällen , in denen Sie wirklich brauchen mehrere Werte, moderne Sprachen in der Regel zurück Tupeln oder irgendeine Art von Datenstruktur (Liste, ein Array - Wörterbuch) oder Objekte sowie irgendeine Form von Destrukturierung binden oder Musteranpassung unterstützen, die Verpackung macht Ihre mehreren Werte in einen einzigen Wert und dann wieder in mehrere Werte trivial destrukturieren.
Hier einige Beispiele für Sprachen, die die Rückgabe mehrerer Werte nicht unterstützen. Ich verstehe nicht wirklich, wie durch das Hinzufügen von Unterstützung für mehrere Rückgabewerte diese deutlich aussagekräftiger werden, um die Kosten einer neuen Sprachfunktion auszugleichen.
Rubin
Python
Scala
Haskell
Perl6
quelle
sort
Funktion:sorted = sort(array)
Gibt nur das sortierte Array zurück, wohingegen[sorted, indices] = sort(array)
beide zurückgegeben werden. Die einzige Möglichkeit, die ich mir in Python vorstellen kann, wäre, eine Flagge nachsort
dem Vorbild vonsort(array, nout=2)
oder zu übergebensort(array, indices=True)
.[a, b, c] = func(some, thing)
) und entsprechend handeln. Dies ist beispielsweise nützlich, wenn die Berechnung des ersten Ausgabearguments billig ist, die Berechnung des zweiten jedoch teuer ist. Ich kenne keine andere Sprache, in der das Äquivalent von Matlabsnargout
zur Laufzeit verfügbar ist.sorted, _ = sort(array)
.sort
Funktion kann erkennen, dass sie die Indizes nicht berechnen muss? Das ist cool, das wusste ich nicht.Der wahre Grund, warum ein einzelner Rückgabewert so beliebt ist, sind die Ausdrücke, die in so vielen Sprachen verwendet werden. In jeder Sprache, in der Sie einen Ausdruck haben können, den
x + 1
Sie bereits als einzelne Rückgabewerte betrachten, weil Sie einen Ausdruck in Ihrem Kopf auswerten, indem Sie ihn in Teile aufteilen und den Wert jedes Teils bestimmen. Sie betrachtenx
und entscheiden, dass der Wert 3 ist (zum Beispiel), und Sie betrachten 1 und dann betrachten Siex + 1
und füge alles zusammen, um zu entscheiden, dass der Wert des Ganzen 4 ist. Jeder syntaktische Teil des Ausdrucks hat einen Wert, keine andere Anzahl von Werten; Das ist die natürliche Semantik von Ausdrücken, die jeder erwartet. Selbst wenn eine Funktion ein Paar von Werten zurückgibt, gibt sie tatsächlich einen Wert zurück, der die Aufgabe von zwei Werten erfüllt, da die Idee einer Funktion, die zwei Werte zurückgibt, die nicht irgendwie in einer einzelnen Sammlung zusammengefasst sind, zu seltsam ist.Die Leute wollen sich nicht mit der alternativen Semantik befassen, die erforderlich wäre, damit Funktionen mehr als einen Wert zurückgeben. In einer stapelbasierten Sprache wie Forth können Sie beispielsweise eine beliebige Anzahl von Rückgabewerten haben, da jede Funktion einfach den oberen Bereich des Stapels ändert und die Eingaben und Ausgaben nach Belieben verschiebt. Deshalb hat Forth nicht die Art von Ausdrücken, die normale Sprachen haben.
Perl ist eine andere Sprache, die sich manchmal so verhält, als würden Funktionen mehrere Werte zurückgeben, obwohl dies normalerweise nur als Rückgabe einer Liste betrachtet wird. Die Art und Weise, wie Listen in Perl "interpoliert" werden, gibt uns Listen,
(1, foo(), 3)
die möglicherweise 3 Elemente enthalten, wie die meisten Leute, die Perl nicht kennen, aber genauso gut nur 2 Elemente, 4 Elemente oder eine größere Anzahl von Elementen haben könnten, je nachdemfoo()
. Listen in Perl sind reduziert, sodass eine syntaktische Liste nicht immer die Semantik einer Liste hat. es kann nur ein Teil einer größeren Liste sein.Eine andere Möglichkeit, Funktionen mehrere Werte zurückgeben zu lassen, wäre eine alternative Ausdruckssemantik, bei der jeder Ausdruck mehrere Werte haben kann und jeder Wert eine Möglichkeit darstellt. Nehmen Sie noch
x + 1
einmal, aber stellen Sie sich diesmal vor, dassx
zwei Werte {3, 4} vorliegen, dannx + 1
wären die Werte von {4, 5} und die Werte vonx + x
{6, 8} oder vielleicht {6, 7, 8}. , abhängig davon, ob für eine Auswertung mehrere Werte verwendet werden dürfenx
. Eine solche Sprache kann mithilfe von Backtracking implementiert werden, ähnlich wie es Prolog verwendet, um eine Abfrage mehrfach zu beantworten.Kurz gesagt, ein Funktionsaufruf ist eine einzelne syntaktische Einheit und eine einzelne syntaktische Einheit hat einen einzelnen Wert in der Ausdruckssemantik, die wir alle kennen und lieben. Jede andere Semantik würde Sie dazu zwingen, seltsame Dinge zu tun, wie Perl, Prolog oder Forth.
quelle
Wie in dieser Antwort vorgeschlagen , handelt es sich um Hardwareunterstützung, wobei auch die Tradition im Sprachdesign eine Rolle spielt.
Von den drei ersten Sprachen, Fortran, Lisp und COBOL, verwendete die erste einen einzelnen Rückgabewert, da dieser nach dem Vorbild der Mathematik erstellt wurde. Der zweite gab eine beliebige Anzahl von Parametern zurück, so wie er sie erhalten hat: als Liste (man könnte auch argumentieren, dass er nur einen einzigen Parameter übergeben und zurückgegeben hat: die Adresse der Liste). Der dritte Wert gibt null oder eins zurück.
Diese ersten Sprachen hatten großen Einfluss auf die Gestaltung der folgenden Sprachen, obwohl Lisp, die einzige, die mehrere Werte zurückgab, nie große Popularität erlangte.
Als C auf den Markt kam, konzentrierte es sich stark auf die effiziente Nutzung von Hardwareressourcen, wobei ein enger Zusammenhang zwischen der Funktionsweise der C-Sprache und dem Maschinencode, der sie implementiert hat, bestand. Einige der ältesten Funktionen, wie "auto" vs "register" -Variablen, sind das Ergebnis dieser Konstruktionsphilosophie.
Es muss auch darauf hingewiesen werden, dass die Assemblersprache bis in die 80er Jahre weit verbreitet war, als sie schließlich aus der allgemeinen Entwicklung ausstieg. Leute, die Compiler schrieben und Sprachen schufen, kannten sich mit Assembler aus und hielten sich größtenteils an das, was dort am besten funktionierte.
Die meisten von dieser Norm abweichenden Sprachen fanden nie große Beliebtheit und spielten daher keine große Rolle bei den Entscheidungen der Sprachgestalter (die sich natürlich von dem inspirieren ließen, was sie wussten).
Sehen wir uns also die Assemblersprache an. Schauen wir uns zunächst den 6502 an , einen 1975er Mikroprozessor, der berühmt für seine Apple II- und VIC-20-Mikrocomputer war. Es war sehr schwach im Vergleich zu dem, was damals in Großrechnern und Minicomputern verwendet wurde, obwohl es im Vergleich zu den ersten Computern vor 20, 30 Jahren zu Beginn der Programmiersprachen leistungsfähig war.
Wenn Sie sich die technische Beschreibung ansehen, hat sie 5 Register und einige Ein-Bit-Flags. Das einzige "volle" Register war der Programmzähler (PC) - dieses Register zeigt auf den nächsten auszuführenden Befehl. Die anderen Register enthalten den Akkumulator (A), zwei "Index" -Register (X und Y) und einen Stapelzeiger (SP).
Durch Aufrufen eines Unterprogramms wird der PC in den Speicher gestellt, auf den der SP zeigt, und der SP wird dann dekrementiert. Die Rückkehr von einem Unterprogramm erfolgt in umgekehrter Reihenfolge. Man kann andere Werte auf dem Stapel verschieben und abrufen, aber es ist schwierig, sich auf den Speicher relativ zum SP zu beziehen, so dass das Schreiben von wiedereintretenden Unterroutinen schwierig war. Diese Sache, die wir für selbstverständlich halten und die wir jederzeit als Unterprogramm aufrufen, war in dieser Architektur nicht so verbreitet. Oft wurde ein separater "Stapel" erstellt, so dass die Parameter und die Subroutinen-Rücksprungadresse getrennt gehalten wurden.
Wenn Sie sich den Prozessor ansehen, der den 6502 und den 6800 inspiriert hat , hat er ein zusätzliches Register, das Indexregister (IX), und das SP, das den Wert vom SP empfangen kann.
Beim Aufrufen einer wiedereintretenden Unterroutine auf dem Computer wurden die Parameter auf dem Stapel abgelegt, der PC abgelegt, der PC auf die neue Adresse geändert, und dann wurden die lokalen Variablen der Unterroutine auf dem Stapel abgelegt . Da die Anzahl der lokalen Variablen und Parameter bekannt ist, kann die Adressierung relativ zum Stapel erfolgen. Eine Funktion, die zwei Parameter empfängt und zwei lokale Variablen hat, würde beispielsweise so aussehen:
Es kann beliebig oft aufgerufen werden, da sich der gesamte temporäre Speicherplatz auf dem Stapel befindet.
Der 8080 , der auf TRS-80 und einer Vielzahl von CP / M-basierten Mikrocomputern verwendet wird, könnte etwas Ähnliches wie der 6800 tun, indem er SP auf den Stapel drückt und ihn dann in sein indirektes Register HL kopiert.
Dies ist eine sehr gebräuchliche Methode, um Dinge zu implementieren, und sie wurde auf moderneren Prozessoren noch mehr unterstützt, und zwar mit dem Basiszeiger, mit dem alle lokalen Variablen vor der Rückkehr einfach gesichert werden können.
Das Problem ist, wie Sie etwas zurückgeben ? Prozessorregister waren zu Beginn nicht sehr zahlreich, und man musste oft einige von ihnen verwenden, um herauszufinden, welcher Teil des Speichers adressiert werden sollte. Das Zurückgeben von Dingen auf den Stack wäre kompliziert: Sie müssten alles platzieren, den PC speichern, die Rückgabeparameter (die in der Zwischenzeit wo gespeichert würden?) Drücken, dann den PC erneut drücken und zurückkehren.
Normalerweise wurde also ein Register für den Rückgabewert reserviert . Der aufrufende Code wusste, dass sich der Rückgabewert in einem bestimmten Register befinden würde, das beibehalten werden musste, bis es gespeichert oder verwendet werden konnte.
Schauen wir uns eine Sprache an, die mehrere Rückgabewerte zulässt: Forth. Forth behält einen separaten Rückgabestapel (RP) und einen separaten Datenstapel (SP) bei, sodass eine Funktion nur alle ihre Parameter abrufen und die Rückgabewerte im Stapel belassen musste. Da der Rückgabestapel getrennt war, störte er nicht.
Als jemand, der Assembler und Forth in den ersten sechs Monaten Erfahrung mit Computern gelernt hat, sind mehrere Rückgabewerte für mich völlig normal. Operatoren wie Forths
/mod
, die die Ganzzahldivision und den Rest zurückgeben, scheinen offensichtlich zu sein. Andererseits kann ich leicht erkennen, wie jemand, dessen erste Erfahrung C mind war, dieses Konzept seltsam findet: Es widerspricht seinen tief verwurzelten Erwartungen, was eine "Funktion" ist.Was Mathematik angeht ... nun, ich habe Computer schon lange programmiert, bevor ich überhaupt in Mathematikunterricht kam. Es gibt eine ganze Sektion von CS und Programmiersprachen, die von Mathematik beeinflusst wird, aber es gibt auch eine ganze Sektion, die dies nicht ist.
Wir haben also einen Zusammenfluss von Faktoren, bei denen die Mathematik das frühe Sprachdesign beeinflusste, bei denen Hardwareeinschränkungen vorschrieben, was leicht zu implementieren war, und bei denen die populären Sprachen die Entwicklung der Hardware beeinflussten (die Maschinenprozessoren Lisp und Forth waren in diesem Prozess Roadkills).
quelle
Die mir bekannten funktionalen Sprachen können durch die Verwendung von Tupeln problemlos mehrere Werte zurückgeben (in dynamisch typisierten Sprachen können Sie sogar Listen verwenden). Tuples werden auch in anderen Sprachen unterstützt:
Im obigen Beispiel
f
gibt eine Funktion 2 Zoll zurück.In ähnlicher Weise können ML, Haskell, F # usw. auch Datenstrukturen zurückgeben (Zeiger sind für die meisten Sprachen zu niedrig). Ich habe noch nie von einer modernen GP-Sprache mit einer solchen Einschränkung gehört:
Schließlich können
out
Parameter auch in funktionalen Sprachen von emuliert werdenIORef
. Es gibt mehrere Gründe, warum es in den meisten Sprachen keine native Unterstützung für Out-Variablen gibt:Unklare Semantik : Gibt die folgende Funktion 0 oder 1 aus? Ich kenne Sprachen, die 0 und 1 ausgeben würden. Beide haben Vorteile (sowohl in Bezug auf die Leistung als auch in Bezug auf das mentale Modell des Programmierers):
Nicht lokalisierte Effekte : Wie im obigen Beispiel können Sie feststellen, dass Sie eine lange Kette haben können und die innerste Funktion den globalen Status beeinflusst. Im Allgemeinen wird es schwieriger, die Anforderungen der Funktion zu ermitteln und festzustellen, ob die Änderung zulässig ist. Angesichts der Tatsache, dass die meisten modernen Paradigmen versuchen, die Effekte zu lokalisieren (Kapselung in OOP) oder die Nebenwirkungen zu eliminieren (funktionale Programmierung), widerspricht sie diesen Paradigmen.
Redundanz : Wenn Sie Tupel haben, haben Sie 99% der Funktionalität von
out
Parametern und 100% der idiomatischen Verwendung. Wenn Sie dem Mix Zeiger hinzufügen, decken Sie die restlichen 1% ab.Ich habe Probleme, eine Sprache zu benennen, die mithilfe eines Tupels, einer Klasse oder eines
out
Parameters nicht mehrere Werte zurückgeben kann (und in den meisten Fällen sind 2 oder mehr dieser Methoden zulässig).quelle
Ich denke, es liegt an Ausdrücken wie
(a + b[i]) * c
.Ausdrücke bestehen aus "singulären" Werten. Eine Funktion, die einen singulären Wert zurückgibt, kann somit anstelle einer der vier oben gezeigten Variablen direkt in einem Ausdruck verwendet werden. Eine Funktion mit mehreren Ausgängen ist in einem Ausdruck zumindest etwas umständlich.
Ich persönlich finde , dass dies ist die Sache , die über einen singulären Rückgabewert Besonderes. Sie können dies umgehen, indem Sie eine Syntax hinzufügen, mit der Sie angeben, welcher der mehreren Rückgabewerte in einem Ausdruck verwendet werden soll. Diese Syntax ist jedoch umständlicher als die gute alte mathematische Notation, die jeder kennt und kurz fasst.
quelle
Dies verkompliziert die Syntax ein wenig, aber es gibt keinen guten Grund auf Implementierungsebene, dies nicht zuzulassen. Im Gegensatz zu einigen anderen Antworten führt die Rückgabe mehrerer Werte, sofern verfügbar, zu einem klareren und effizienteren Code. Ich kann nicht zählen, wie oft ich mir gewünscht habe, ein X und ein Y oder einen "Erfolg" -Booleschen Wert und einen nützlichen Wert zurückgeben zu können.
quelle
[out]
Parameter, aber praktisch alle geben einenHRESULT
(Fehlercode) zurück. Es wäre sehr praktisch, dort ein Paar zu finden. In Sprachen, die Tupel gut unterstützen, wie z. B. Python, wird dies in vielen Codes verwendet, die ich gesehen habe.sort
Funktion normalerweise ein Array sortiert:sorted_array = sort(array)
. Manchmal brauche ich auch die entsprechende Indizes:[sorted_array, indices] = sort(array)
. Manchmal möchte ich nur, dass die Indizes:[~, indices]
= sort (array). The function
sort` tatsächlich angeben können, wie viele Ausgabeargumente benötigt werden. Wenn also für 2 Ausgaben im Vergleich zu 1 zusätzliche Arbeit erforderlich ist, können diese Ausgaben nur bei Bedarf berechnet werden.In den meisten Sprachen, in denen Funktionen unterstützt werden, können Sie einen Funktionsaufruf überall dort verwenden, wo eine Variable dieses Typs verwendet werden kann:
Wenn die Funktion mehr als einen Wert zurückgibt, funktioniert dies nicht. Dynamisch getippte Sprachen wie Python ermöglichen dies. In den meisten Fällen wird jedoch ein Laufzeitfehler ausgegeben, es sei denn, es kann etwas Sinnvolles mit einem Tupel in der Mitte einer Gleichung herausgearbeitet werden.
quelle
foo()[0]
Ich möchte nur auf Harveys Antwort aufbauen. Ich fand diese Frage ursprünglich auf einer Nachrichtentechnologieseite (arstechnica) und fand eine erstaunliche Erklärung dafür, dass ich der Meinung bin, dass sie den Kern dieser Frage wirklich beantwortet und allen anderen Antworten (außer denen von Harvey) fehlt:
Der Ursprung der Einzelrückgabe von Funktionen liegt im Maschinencode. Auf der Maschinencodeebene kann eine Funktion einen Wert im A-Register (Akkumulator) zurückgeben. Alle anderen Rückgabewerte befinden sich auf dem Stapel.
Eine Sprache, die zwei Rückgabewerte unterstützt, kompiliert diesen als Maschinencode, der einen zurückgibt, und legt den zweiten auf den Stapel. Mit anderen Worten, der zweite Rückgabewert würde ohnehin als out-Parameter enden.
Es ist, als würde man fragen, warum die Zuweisung jeweils eine Variable ist. Sie könnten beispielsweise eine Sprache haben, die a, b = 1, 2 zulässt. Aber es würde auf der Maschinencodeebene enden, die a = 1 gefolgt von b = 2 ist.
Es gibt einige Gründe dafür, dass Konstrukte von Programmiersprachen einen gewissen Einfluss darauf haben, was tatsächlich passiert, wenn der Code kompiliert und ausgeführt wird.
quelle
Es begann mit Mathe. FORTRAN, benannt nach "Formula Translation", war der erste Compiler. FORTRAN war und ist auf Physik / Mathematik / Technik ausgerichtet.
COBOL, fast so alt, hatte keinen expliziten Rückgabewert; Es gab kaum Unterprogramme. Seitdem ist es größtenteils Trägheit.
Go hat beispielsweise mehrere Rückgabewerte, und das Ergebnis ist klarer und weniger mehrdeutig als die Verwendung von "out" -Parametern. Nach ein wenig Gebrauch ist es sehr natürlich und effizient. Ich empfehle, für alle neuen Sprachen mehrere Rückgabewerte zu berücksichtigen. Vielleicht auch für alte Sprachen.
quelle
Dies hat wahrscheinlich mehr damit zu tun, wie Funktionsaufrufe in Prozessormaschinenanweisungen vorgenommen werden und dass alle Programmiersprachen vom Maschinencode abgeleitet sind, z. B. C -> Assembly -> Machine.
Wie Prozessoren Funktionsaufrufe ausführen
Die ersten Programme wurden in Maschinencode geschrieben und später zusammengesetzt. Die Prozessoren unterstützten Funktionsaufrufe, indem sie eine Kopie aller aktuellen Register in den Stapel schoben. Wenn Sie von der Funktion zurückkehren, wird der gespeicherte Registersatz vom Stapel gelöscht. Normalerweise wurde ein Register unberührt gelassen, damit die Rückgabefunktion einen Wert zurückgeben konnte.
Nun, warum die Prozessoren so entworfen wurden ... war es wahrscheinlich eine Frage der Ressourcenbeschränkungen.
quelle