Du kannst nicht nur einer Funktion Infos mit auf den Weg geben, sondern kannst auch eine Info zurückbekommen. Das ist der Rückgabewert.
Beim Beipspiel mit dem Roboter, der ein Parkticket kaufen soll, gibt es auch einen Rückgabewert. Was meinst du könnte dar Rückgabewert sein? Was entsteht dabei, wenn der Roboter die Befehle ausführt? Das Parkticket natürlich!
Rückgabetyp definieren
Nehmen wir an, du möchtest eine komplexe Rechnung mit einer Funktion abhandeln, z.B. die Quadratzahl einer Zahl zu berechnen. Dann definierst du:
float quadratzahl(float x) {
float erg = x * x;
return erg;
}
Du siehst hier zwei neue Dinge:
- ... steht da statt "void" jetzt "float".
- ... steht da das Wort "return".
Das float bedeutet, dass diese Funktion einen Wert vom Typ float zurückgibt. Wohingegen void (engl. für "nichts") bedeutet, dass eine Funktion keinen Wert zurückgibt.
Damit du Processing sagen kannst, was genau zurückgegeben werden soll (und wann), gibt es das Schlüsselwort return. Im Beispiel definierst du eine neue lokale Variable namens erg. Sobald Processing auf das Wort return trifft, wird der Wert von erg zurückgegeben und es werden keine weiteren Zeilen mehr verarbeitet.
Das heißt, jeglicher Code unterhalb von return ist sinnlos:
float quadratzahl(float x) {
float erg = x * x;
return erg; // Processing gibt Wert zurück und bricht ab
println("bin ich jetzt dran?"); // wird nie ausgeführt
}
Tipp: Hinter return können auch ganze Ausdrücke stehen, d.h. in kurz wäre es so:
float quadratzahl(float x) {
return x * x;
}
Vielleicht noch ein zweites Code-Beispiel, das etwas interessanter ist: Du möchtest testen, ob ein Punkt (x, y) innerhalb eines Rechtecks (rectX, rectY, rectWidth, rectHeight) liegt (die vier Werte definieren Eckpunkt, Breite und Höhe), z.B. um eine Kollision in einem Spiel zu gestalten. Du definierst dazu eine Funktion, die das testet und wahr/falsch zurückgibt, also einen boolean :
boolean imRechteck(int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
if (x > rectX && x < rectX + rectWidth && y > rectY && y < rectY + rectHeight) {
return true;
} else {
return false;
}
}
Wieder der Tipp, dass hinter return auch Ausdrücke stehen können. Die If-Bedingung ist nichts anderes als ein boolescher Ausdruck, der zu true oder false ausgewertet wird. Das heißt, du kannst direkt schreiben:
boolean imRechteck (int x, int y, int rectX, int rectY, int rectWidth, int rectHeight) {
return (x > rectX && x < rectX + rectWidth && y > rectY && y < rectY + rectHeight);
}
Return garantieren
Processing besteht darauf, dass eine Funktion mit Rückgabewert immer auf ein return trifft. Das wird dann wichtig, wenn du z.B. ein if im Code hast, denn hier werden ja mitunter Code-Teile übersprungen, d.h. dein return wird vielleicht nie erreicht.
Schauen wir uns eine einfache Funktion an, die prüft, ob eine Zahl positiv ist:
boolean isPositive(int num) {
if (num >= 0) {
return true;
}
}
Processing weigert sich, diesen Code auszuführen, denn es ist nicht garantiert, dass das return erreicht ist. Was, wenn die Zahl negtiv ist? Den Mangel beheben wir entweder so:
boolean isPositive(int num) {
if (num >= 0) {
return true;
} else {
return false;
}
}
Oder so:
// Alterantive Lösung ohne Else
boolean isPositive(int num) {
if (num >= 0) {
return true;
}
return false;
}
In beiden Fällen wird immer ein return erreicht. Und jetzt schauen wir uns an, wie du diese Funktionen aufrufst.
Rückgabewerte im Code verwenden
Beim Aufruf einer Funktion mit Rückgabewert wird der Aufruf durch den Rückgabewert ersetzt. Insofern ähnelt dies dem Prinzip einer Variable, die durch ihren Inhalt ersetzt wird.
Aus
println(quadratzahl(5));
wird im nächsten Schritt also
println(25);
Rückgabewerte werden häufig zunächst an Variablen gebunden und dann weiter verarbeitet:
float z = quadratzahl(5);
println("Ergebnis: " + z);
Du kannst aber den Funktionsaufruf auch direkt als Parameter für eine andere Funktion (hier wäre das println) einbinden:
println("Ergebnis: " + quadratzahl(5));
Hier ein Beispiel mit der zweiten Funktion:
boolean collision = imRechteck(25, 40, 20, 30, 100, 100);
if (collision) {
println("kaputt!");
}
Auch hier könntest du den Funktionsaufruf direkt in die If-Bedingung hineinsetzen.
if (imRechteck(25, 40, 20, 30, 100, 100)) {
println("kaputt!");
}
Allgemein solltest du einen Funktionsaufruf mit Rückgabewert genauso betrachten wie eine Variable. Eine Variable vom Typ int kann überall auftreten, wo ein int -Wert stehen könnte. Genauso kann ein Funktionsaufruf mit Rückgabetyp int überall dort stehen, wo ein int -Wert stehen könnte.
Beachte die syntaktische Ähnlichkeit von Variablendeklaration und Funktionsdefinition:
float foo;
float quadratzahl(float x) {
return x * x;
}
Beide Konstrukte, Variable und Funktion, stehen stellvertretend für einen Wert von einem bestimmten Typ. Daher können beide an den gleichen Stellen stehen.
Zusammenfassung
Funktionen können einen Wert zurückgeben und somit ihrer Benennung als "Funktion" gerecht werden, denn auch mathematische Funktionen erzeugen einen Wert, wenn man ihnen einen Parameter übergibt.
Bei der Definition einer Funktion mit Rückgabewert muss man den Typ des Rückgabewerts angeben. Dieser Typ steht vor dem Funktionsnamen.
float quadratzahl(float x) {
// ...
}
Im Code der Funktion muss jeder mögliche Verlauf in einer Zeile enden, die ein return ausführt. Hier wird spezifiziert, welcher Wert zurückgeliefert wird. Der Typ des Werts muss natürlich mit dem Typ übereinstimmen, der in der Kopfzeile angegeben ist.
float quadratzahl(float x) {
float erg = x * x;
return erg;
}
Häufiger Fehler ist, dass bei Verwendung von z.B. if vergessen wird, dass der Code verschiedene Wege nehmen kann.
float never42(float x) {
if (x != 42) {
return x;
}
// FEHLER: kein return für den Fall x == 42
}
Beim Aufruf einer Funktion mit Rückgabewert wird der Aufruf durch den Rückgabewert ersetzt ähnlich einer Variable, deren Aufruf durch ihren Wert ersetzt wird.