# Mehrere Return-Befehle in einer Methode



## SteeL1942 (19. Jun 2010)

Wir schreiben in der Schule zur Zeit ein Program zum potenzieren. An sich macht mir das keine Probleme. Habe dazu eine rekursive Methode geschrieben:


```
int PotenzRekursiv (int basis, int exponent)
		{
			if(exponent<0)
			{
				return 1/PotenzRekursiv(basis,exponent*-1);
			}
			if(exponent==0)
			{
				return 1;
			}
			else
			{
				return basis*PotenzRekursiv(basis,exponent-1);
			}
		}
```

Jetzt meinte aber mein Lehrer, die sei einfach nur schlecht programmiert, weil da mehrere Return-befehle drin sind und dass man sowas einfach nicht macht. Aber ich finde, dass es schon sinn macht, denn mit einem return endet ja die methode und unter umständen spart man dann ja programmlaufzeit, wenn der return-befehl schon weiter oben ausgeführt wird. Mein Lehrer will die Methode aber so haben:


```
int PotenzRekursiv (int basis, int exponent)
		{
			int Erg;
			if(exponent<0)
			{
				Erg = 1/PotenzRekursiv(basis,exponent*-1);
			}
			if(exponent==0)
			{
				Erg = 1;
			}
			else
			{
				Erg = basis*PotenzRekursiv(basis,exponent-1);
			}
			return Erg;
		}
```

Was ist denn nun "besser" ?


----------



## agentone (19. Jun 2010)

Wenn du das Ergebnis kennst, und du dir sicher bist, dass es sich nicht mehr ändert, dann gibst du es sofort zurück! Damit sparst du Speicherplatz und Laufzeit, wenn auch nur gering.

Bei der Methode deiner Lehrerin muss extra eine Variable deklariert werden, was in diesem einfachen Fall völlig unnötig ist. Vermutlich hat sie sich an diese Variante gewöhnt, und will nun, dass du das übernimmst. Ich finde aber deine Variante deutlich besser!


----------



## Der Müde Joe (19. Jun 2010)

Mal Abgesehen davon das return kein = hat und ich ein if() else if() else machen würde, benutze ich ganz klar die return Variante.

Von Standpunkt des Profs sieht er in jedem return ein goto. Wie gings noch gleich:
Goto Statements considered harmful (Dijkstra)


----------



## SteeL1942 (19. Jun 2010)

Der Müde Joe hat gesagt.:


> Mal Abgesehen davon das return kein = hat



upps. Joa habs mal weg gemacht


----------



## maki (19. Jun 2010)

Sehe das wie der Müde Joe und der Lehrer, es gibt Ausnahmen, zB. wenn die (komplexe) Verarbeitung gleich abgebrochen werden kann., weil zB. eine unkritische Vorbedingung nicht erfüllt ist.

agentone, das mit dem Speicherverbrauch & der Laufzeit meinst du aber nicht ernst, oder??


----------



## Wortraum (19. Jun 2010)

Ich kreide da zwei andere Dinge an, sowohl bei Deiner als auch bei der Lösung des Lehrers.
1) Es fehlt ein Zugriffsmodifikator.
2) Methodennamen schreibt man am Anfang klein.

Welche Lösung Du im speziellen bevorzugst, ist ansonsten eine Frage des Geschmackes, denn hier ist sehr klar, was passiert. Im allgemeinen ist es aber besser, wenn man nicht an irgendwelchen x-beliebigen Stellen aus einer Methode springt.

Ich hätte es wohl so geschrieben:

```
public int potenzRekursiv(int basis, int exponent) {
    int result = 1;
    if (exponent < 0)
    {
        result = 1 / potenzRekursiv(basis, exponent * -1);
    }
    else if (exponent > 0)
    {
        result = basis * potenzRekursiv(basis, exponent - 1);
    }
    return result;
}
```


----------



## Illuvatar (19. Jun 2010)

maki hat gesagt.:


> Sehe das wie der Müde Joe und der Lehrer



Wenn ich das richtig versteh, sieht es der müde Joe aber anders als der Lehrer 
Noch meine Meinung: die Version ohne extra Variable find ich besser lesbar. Richtig ist aber auch, dass es im Allgemeinen nicht gut ist, mitten aus einer Methode rauszuspringen.

Wenn ich mir überleg wo ich die Grenze ziehen würde: ich denke, innerhalb von Schleifen sollte man kein return stehen haben, und außerhalb von Schleifen nicht in tiefen Verschachtelungen und nicht in der Mitte einer langen Methode (natürlich sollte es im Optimalfall sowieso keine so langen Methoden geben).
Irgendwo ist es aber Geschmackssache, und an deiner Stelle würd ich den Lehrer nicht verärgern...


----------



## SteeL1942 (19. Jun 2010)

Illuvatar hat gesagt.:


> und an deiner Stelle würd ich den Lehrer nicht verärgern...



hatte ich auch nicht vor. wollte es nur mal so für mich wissen, weil er halt immer sagt, dass wir nicht so umständlich programmieren sollen um "laufzeit zu sparen" und das hab ich ja damit gemacht, weil die methode früher enden kann und ich die eine variable "spare". bringt zwar bei so einen 0815-Programm recht wenig, aber wenn ich programme habe, die wesentlich mehr rechnen und verarbeiten, kann man das doch schon merken


----------



## Wortraum (19. Jun 2010)

SteeL1942 hat gesagt.:


> bringt zwar bei so einen 0815-Programm recht wenig, aber wenn ich programme habe, die wesentlich mehr rechnen und verarbeiten, kann man das doch schon merken


Ja, klar, an Fehlern und schlechter wartbarem Quelltext. 

Laß Dir doch nicht so einen Unsinn einreden!


----------



## Gast2 (19. Jun 2010)

Ich sehe es so wie Illuvatar. Zudem meckern viele statische Codechecker wie z.B. PMD über mehrere return-Statements.


----------



## maki (19. Jun 2010)

Illuvatar hat gesagt.:


> Wenn ich das richtig versteh, sieht es der müde Joe aber anders als der Lehrer
> Noch meine Meinung: die Version ohne extra Variable find ich besser lesbar. Richtig ist aber auch, dass es im Allgemeinen nicht gut ist, mitten aus einer Methode rauszuspringen.
> 
> Wenn ich mir überleg wo ich die Grenze ziehen würde: ich denke, innerhalb von Schleifen sollte man kein return stehen haben, und außerhalb von Schleifen nicht in tiefen Verschachtelungen und nicht in der Mitte einer langen Methode (natürlich sollte es im Optimalfall sowieso keine so langen Methoden geben).
> Irgendwo ist es aber Geschmackssache, und an deiner Stelle würd ich den Lehrer nicht verärgern...



Ach, hatte Joes Beitrag falsch gelesen... 

Die Regel "Ein Return pro Methode" ist eine sehr alte (PL1 und konsorten), man ging damals davon aus, das Methoden lang & unübersichtlich sind, und mehrere Return die Sache noch komplizierter machen.
Heute ist das ein bisschen anders, Marco13 hatte mal einen Thread dazu, man "darf" schon am Anfang gleich mit einem Return rausspringen, vor allem wenn man sich dadurch überflüssige if/else konstrukte spart, das kommt immer darauf an.

Was allerdings vollkommener quatsch ist, ist die Aussage das man ohne die temp. Variablen Speicher & Laufzeit sparen würde... :autsch:


----------



## xerberuz (19. Jun 2010)

Ich finde ja auch die Variante mit mehreren return statements besser. Wenn schon eine extra Variable dafür definiert wird, sollte diese zumindest final sein. Das verhindert mehrfache Zuweisungen welche ich für unglaublich Unübersichtlich halte. Ohne die mehrfache Zuweisung kann der Compiler die dann auch gleich wieder wegoptimieren.


----------



## SteeL1942 (19. Jun 2010)

maki hat gesagt.:


> Was allerdings vollkommener quatsch ist, ist die Aussage das man ohne die temp. Variablen Speicher & Laufzeit sparen würde... :autsch:



naja, aber irgendwas muss der rechner da ja auch machen, und wenn das wegfällt, kann er da was anderes machen, ist also im endeffekt minimal schneller. hatte ja oben gesagt, dass es jetzt in diesem fall nutzlos ist, aber das muss ja nicht immer so sein... wenn ich damit jetzt 2 hoch 1000 rechnen wollte, müsste er diese methode ja 1000 mal aufrufen und folglich auch 1000 variablen erstellen, wenn ich das direkt mit return mache, wird nicht eine einzige erstellt


----------



## Landei (19. Jun 2010)

Bei sehr kurzen Methoden würde ich deine Variante durchgehen lassen. Aber mehrere returns machen es schwer, dem Programmfluss zu folgen, wenn die Methode etwas komplizierter wird. So will man z.B. "unten" noch eine bestimmte Aktion einfügen, übersieht aber, dass die Methode in machen Versionen schon beendet ist, was manchmal einen schwer zu fndendender Bug ist (denn auf den ersten Blick sieht ja alles "richtig" aus). Eine gute Lösung ist in solchen Fällen oft, die Methode in einen öffentlichen "Initialisierungsteil" und einen privaten "Arbeitsteil" aufzuteilen:


```
public int potenz(int basis, int exponent) {
  return exponent >= 0 
    ? potenzRekursiv(basis,exponent)
    : 1 / potenzRekursiv(basis, -exponent);
}

private int potenzRekursiv(int basis, int exponent) { 
   return (exponent == 0)  ? 1
     : basis * potenzRekursiv(basis, exponent-1);
}
```


----------



## Wortraum (19. Jun 2010)

SteeL1942 hat gesagt.:


> wenn ich damit jetzt 2 hoch 1000 rechnen wollte, müsste er diese methode ja 1000 mal aufrufen und folglich auch 1000 variablen erstellen, wenn ich das direkt mit return mache, wird nicht eine einzige erstellt


1000 Aufrufe? Nimm vielleicht 1 000 000 000, dann kann man etwas messen, und ich habe es aus Lust und Laune sogar ausprobiert:

```
public class Main {
    public static int a(int i) {
        if (i < 0)
            return -1;
        else if (i > 0)
            return 1;
        return 0;
    }

    public static int b(int i) {
        int result = 0;
        if (i < 0)
            result = -1;
        else if (i > 0)
            result = 1;
        return result;
    }

    public static void main(String[] args) throws InterruptedException {
        int numCalls = (int) 1E9;
        int half = numCalls / 2;

        Thread.sleep(500);
        long startA = System.currentTimeMillis();
        for (int i = -half; i < half; ++i)
            a(i);
        long endA = System.currentTimeMillis();
        long diffA = endA - startA;
        System.out.println("a(i): " + diffA);
	
        System.gc();
        Thread.sleep(500);
        long startB = System.currentTimeMillis();
        for (int i = -half; i < half; ++i)
            b(i);
        long endB = System.currentTimeMillis();
        long diffB = endB - startB;
        System.out.println("b(i): " + diffB);
        System.out.println("Diff: " + Math.abs(diffA - diffB));
    }
}
```
Die Ergebnisse sind recht konstant, auch wenn man b(i) vor a(i) aufruft; hier sind zwei der Ergebnisse:
a(i): 2816
b(i): 3028
Diff: 212       

a(i): 2819
b(i): 3036
Diff: 217

Was sagt und dieses Ergebnis? Es sagt uns, daß es hier überhaupt nichts zu optimieren gibt. Wenn man etwas optimieren möchte, dann viel später und dann bei wichtigen Algorithmen, bei den wahren Flaschenhälsen, etwa bei Sortieralgorithmen oder großen Datenstrukturen.

Viel wichtiger ist hingegen, selbsterklärenden Quelltext zu schreiben. In meinem Beispiel sieht man, daß ich startA und endA nicht wiederverwende, sondern neue Variablen für den zweiten Testlauf einrichte. Es ist zwar kein glanzvolles Beispiel, doch allgemein gilt: lieber zusätzliche Variablen mit gut ausgewählten Namen verwenden, so daß man selbsterklärenden Quelltext schreibt! Es kommt in erster Linie auf die Verständlichkeit an.

Ein Aufruf einer Methode ist auch nicht kostenlos, und doch sind mehrere kleine Methoden besser lesbar als endlose Zeilenmonster, von denen man ebenfalls nur abraten kann. Die Hälfte der Zeit fällt anbei allein für die Schleifendurchläufe und Methodenaufrufe an.


----------



## SteeL1942 (19. Jun 2010)

Ok, also ich versuch das ganze mal für zusammenzufassen:

Mehrere Returns sind nicht falsch, wenn die Methode klein ist und ein außenstehender die methode immernoch leicht durchblicken kann.
Bei größeren und vor allem komplexen Methoden sollte man mehrfache returns vermeiden.
Abschließend muss man noch festhalten, dass solche winz-"optimierungen" das programm nur unmerklich beschleunigen.

Hab ich was vergessen bzw falsch verstanden??


----------



## Wortraum (19. Jun 2010)

SteeL1942 hat gesagt.:


> Abschließend muss man noch festhalten, dass solche winz-"optimierungen" das programm nur unmerklich beschleunigen.


Gar nicht, behaupte ich. Große Programme, bei denen Optimierung angebracht ist, verwenden oft zahlreiche Threads, haben viele IO‐Zugriffe auf das Netz, Datenbanken, Festplatten und hängen von so vielen Dingen ab, daß man dort nicht einmal einen Unterschied messen können wird. Bei obigem Beispiel handelt es sich ja lediglich um eine Konstruktion, die fernab praktischen Einsatzes ist.

Häßlich wird _return_ dann, wenn man beispielsweise unter einer bestimmten Bedingung in einer Schleife aus der Methode zurückkehrt, andernfalls die Schleife weiterläuft und anschließend noch andere Methoden aufgerufen werden oder noch irgend etwas gemacht wird. Der nächste, der dann etwas ändern möchte, baut vielleicht noch ein _return _hinzu, und mit der Zeit wird die Methode größer und unübersichtlicher.

In Deinem Fall finde ich es völlig in Ordnung, und es kann auch lesbarer und klarer sein. Wenn man sich mit einem _return_ weitere Verschachtelungen sparen kann, macht es das ja auch übersichtlicher.


----------



## maki (19. Jun 2010)

SteeL1942 hat gesagt.:


> ..
> Abschließend muss man noch festhalten, dass solche winz-"optimierungen" das programm nur unmerklich beschleunigen.
> 
> Hab ich was vergessen bzw falsch verstanden??


Den Teil hast du komplett falsch verstanden 

Die zusätzliche Variable macht keinen Unterschied, so einfach ist das.
Solche Pseudo-Optimierungen bringen doch gar nix, schon gar nicht darf man sein Programm danach  strukturieren.
Wenn du Optimieren willst, musst du messen.


----------



## SteeL1942 (20. Jun 2010)

maki hat gesagt.:


> Solche Pseudo-Optimierungen bringen doch gar nix, schon gar nicht darf man sein Programm danach  strukturieren.



Also muss ich sowas immer so nach gefühl machen? Wenn die methode unübersichtlich und groß wird, nur ein return und sonst auch mal mehrere, wenn es sinn macht und keine schleifen unterbrochen werden?


----------



## Gast2 (20. Jun 2010)

SteeL1942 hat gesagt.:


> Also muss ich sowas immer so nach gefühl machen?



Jeder hat da seine eigenen Regeln, z.B. haben viele Firmen festgelegte Coding Conventions in denen soetwas geregelt ist. Wichtig ist das dein Code leicht und eindeutig verständlich, gut strukturiert und meiner Meinung nach konsitent ist. Schlimm finde ich es immer wenn ein Entwickler sich dauernd umentscheided bei solchen Dingen.


----------



## slawaweis (20. Jun 2010)

ob mehrere Return-Anweisungen oder nur eine am Ende ist nicht die Frage. Sondern wie man seine gesamte Software schreibt und sich konsequent an bestimmte Muster hält. Man kann ruhig mehrere Return-Anweisungen in einer größeren Funktion schreiben, auch aus Schleifen heraus (z.B. der klassische Fall, wo ein bestimmtes Element in einer Liste gesucht wird). Nicht mal GOTO ist ein Problem, wenn man es richtig und konsequent anwendet. Das Argument mit komplexen Code ist ein schwaches, den dann ist entweder der Code schlecht geschrieben oder die anderen Programmierer haben diesen nicht ausreichend gelesen. Überhaupt sollte der Fall, dass mehrere Programmierer regelmäßig an der selben Funktion werkeln, nur die Ausnahme sein.

So können ruhig mehrere Return-Anweisungen in einer Methode oder Funktion stehen. Doch es sollte schon aus der Beschreibung/Bedeutung der Funktion ersichtlich sein, dass es da logische Blöcke gibt, die bei bestimmten Bedienungen die Abarbeitung der Funktion vorzeitig beenden. Um auf das Beispiele aus dem ersten Post zurückzukommen, ich hätte es wie Landei mit dem Triple-Operator ?: gelöst und nur einer Return-Anweisung. Vielleicht war das genau der Punkt, den der Lehrer meinte. Potenzen und Fakultäten sind sowieso das klassische Beispiel für ?: und Rekursion während der Lehre.



SteeL1942 hat gesagt.:


> Also muss ich sowas immer so nach gefühl machen? Wenn die methode unübersichtlich und groß wird, nur ein return und sonst auch mal mehrere, wenn es sinn macht und keine schleifen unterbrochen werden?


nicht nach Gefühl, sondern nach Erfahrung und im Team nach bestimmten vorher festgelegten Mustern oder Richtlinien. Am Ende muss die Software nach den Anforderungen korrekt funktionieren und gut wartbar sein, vom selben Team oder einem anderen.

Slawa


----------



## Tobias (20. Jun 2010)

Viel wichtiger als die Optimierung deines Codes nach irgendwelchen gedachten Laufzeiten ist es, dass dein Code verständlich und leicht wartbar ist. Stell dir vor, du legst das Programm ein Jahr in die Schublade und musst dann etwas daran ändern: Sind die Programmstruktur und die Abläufe so klar ausgedrückt, dass du die Änderungen "nach Gefühl", sprich ohne jeden Schritt durchgegangen zu sein, machen kannst? Nein? Dann schreib es klarer hin, auch wenn das im Zweifelsfall bedeutet, dass du mehrere, kleine Methoden anlegen musst oder zusätzliche Variablen einführen musst o. ä.

EDIT:



> Überhaupt sollte der Fall, dass mehrere Programmierer regelmäßig an der selben Funktion werkeln, nur die Ausnahme sein.



Schon richtig. Aber ich weiß nach einem Jahr garantiert nicht mehr, wie die Methode im Detail funktioniert. Da hätte sie auch jemand anderes geschrieben haben können ...


----------



## agentone (20. Jun 2010)

> agentone, das mit dem Speicherverbrauch & der Laufzeit meinst du aber nicht ernst, oder??





Ich hab nochmal drüber nachgedacht: Der Stack wird durch eine zusätzliche Variable weder größer noch kleiner und die Laufzeit ändert sich auch nicht. Allerdings habe ich mir angewöhnt, nur dann Hilfsvariablen einzuführen, wenn diese mir auch etwas bringen. Beispielsweise wird:

```
double sin=Math.sin(var);
for(int i=0; i<100000; i++)
{
 //mach irgendwas mit sin
}
```
deutlich kürzer laufen als:

```
for(int i=0; i<100000; i++)
{
 double sin=Math.sin(var);
 //mach irgendwas mit sin
}
```
In diesem Fall würde ich also eine Hilfsvariable einiges bringen: kürzere Laufzeit, da nicht immer wieder dieselbe Berechnung ausgeführt wird und der gc deutlich weniger zu tun hat. (Ich bin jetzt mal davon ausgegangen, dass die JVM keine Laufzeit-Optimierung durchführt, was glaube ich nicht stimmt.)

Und wenn ich eine Schleife habe, in der ich ein Array durchlaufe, und prüfe, ob ein Element eine bestimmte Bedingung erfüllt, damit ich dieses dann zurückgeben kann, gebe ich dieses Element immer sofort mit return zurück. In diesem Fall spare ich dann aber Laufzeit!

```
for(int i=0; i<array.length; i++)
{
 if(array[i]!=null){return array[i];}
}
```
anstatt

```
Object obj=null;
for(int i=array.length-1; i>=0; i--)
{
 if(array[i]!=null){obj=array[i];}
}
return obj;
```

Obwohl ich in diesem Fall nicht wüsste, ob es besser wäre eine Hilfsvariable foo=array_ und length=array.length einzuführen?

Aber ich immernoch glaube, dass es im einfachen Beispiel der Potenzierung übersichtlicher ist, sofort return zu schreiben._


----------



## SteeL1942 (20. Jun 2010)

joa das mit dem umentscheiden hab ich auch manchmal. kann mich nie entscheiden, ob die methoden jetzt einen deutschen oder englischen namen kriegen sollen...

EDIT: Bin jetzt mal mein Programm durchgegangen, was ich grade schreibe und da hab ich zum teil doch große methoden und an einigen stellen weis ich auch nicht mehr, was da passiert, weil ich an dem programm schon seit januar dran sitze... Einige sachen hab ich halt schon zu anfang fertig gehabt und dann musste ich da nicht mehr dran.


----------

