![]() |
|
|||||
Äquivalenz einer while- und einer do/while-SchleifeDie do-Schleife wird seltener gebraucht als die while-Schleife. Dennoch lassen sich beide ineinander überführen. Zunächst der erste Fall. Wir ersetzen eine while-Schleife durch eine do/while-Schleife: while ( Ausdruck ) Anweisung Führen wir uns noch einmal vor Augen, was hier passiert. In Abhängigkeit vom Ausdruck wird der Rumpf ausgeführt. Da zunächst ein Test kommt, wäre die do/while-Schleife schon eine Blockausführung weiter. So fragen wir in einem ersten Schritt mit einer if-Anweisung ab, ob die Bedingung wahr ist oder nicht. Wenn ja, dann lassen wir den Programmcode in einer do/while-Schleife abarbeiten. Die äquivalente do/while-Schleife sieht also wie folgt aus: if ( Ausdruck ) do Anweisung while ( Ausdruck ) ; Nun der zweite Fall. Wir ersetzen die do/while-Schleife durch eine while-Schleife: do Anweisung while ( Ausdruck ) ; Da zunächst die Anweisungen ausgeführt werden und anschließend der Test, schreiben wir für die while-Variante die Ausdrücke einfach vor den Test. So ist sichergestellt, dass diese zumindest einmal abgearbeitet werden. Anweisung while ( Ausdruck ) Anweisung 2.6.4 Die for-Schleife
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Beispiel Gebe die Zahlen von 1 bis 10 auf dem Bildschirm aus
for ( int i = 1; i <= 10; i++ ) System.out.println( i ); |
Eine genauere Betrachtung der Schleife zeigt die unterschiedlichen Segmente:
| Initialisierung der Schleife Der erste Teil der for-Schleife ist ein Ausdruck wie i = 1, der vor der Durchführung der Schleife genau einmal ausgeführt wird. Dann wird das Ergebnis verworfen. Tritt in der Auswertung ein Fehler auf, so wird die Abarbeitung unterbrochen und die Schleife kann nicht vollständig ausgeführt werden. Der erste Teil kann lokale Variablen definieren und initialisieren. Diese Zählvariable ist dann außerhalb des Blocks nicht mehr gültig.2 Es darf noch keine lokale Variable mit dem gleichen Namen geben. |
| Schleifentest/Schleifenbedingung Der mittlere Teil, wie i <= 10, wird vor dem Durchlaufen des Schleifenrumpfs - also vor jedem Schleifeneintritt - getestet. Ergibt der Ausdruck false, wird die Schleife nicht durchlaufen und beendet. Das Ergebnis muss, wie bei einer while-Schleife, vom Typ boolean sein. Ist kein Test angegeben, so ist das Ergebnis automatisch true. |
| Schleifen-Inkrement durch einen Fortschaltausdruck Der letzte Teil, wie i++, wird immer am Ende jedes Schleifendurchlaufs, aber noch vor dem nächsten Schleifeneintritt ausgeführt. Das Ergebnis wird nicht weiter verwendet. Ergibt die Bedingung des Tests true, dann befindet sich beim nächsten Betreten des Rumpfs der veränderte Wert im Rumpf. |
Betrachten wir das Beispiel, so ist die Auswertungsreihenfolge folgender Art:
| 1. | Initialisiere i mit 1 |
Wird die for-Schleife zum Durchlaufen einer Variablen genutzt, so heißt der Schleifenzähler entweder Zählvariable oder Laufvariable.
// Zahlen von 1 bis 10 ausgeben for ( int i = 1; i <= 10; i++ ) // i ist Schleifenzähler System.out.println( i );
Wichtig ist die Initialisierung und die korrekte Abfrage am Ende. Schnell läuft die Schleife einmal zu oft durch und führt so zu falschen Ergebnissen. Die Fehler bei der Abfrage werden auch »off-by-one error» genannt, wenn zum Beispiel anstatt <= der Operator < steht. Dann nämlich läuft die Schleife nur bis 9. Ein anderer Name für den Schleifenfehler heißt »fencepost error». Es geht um die Frage, wie viele Pfähle nötig sind, um 100 m einzuzäumen, sodass alle Pfähle einen Abstand von 10 m haben: 9, 10, 11?
Da sich while- und for-Schleife sehr ähnlich sind, besteht die berechtigte Frage, wann die eine und wann die andere zu nutzen ist. Leider verführt die kompakte for-Schleife sehr schnell zu einer Überladung. Manche Programmierer packen gerne alles in den Schleifenkopf hinein, und der Rumpf besteht nur aus einer leeren Anweisung. Dies ist ein schlechter Stil und muss vermieden werden. Die for-Schleife sollte dort eingesetzt werden, wo sich alle drei Ausdrücke im Schleifenkopf auf dieselbe Variable beziehen. Vermieden werden sollten unzusammenhängende Ausdrücke im Schleifenkopf.
Da alle drei Ausdrücke im Kopf der Schleife optional sind, können sie weggelassen werden und es ergibt sich eine Endlosschleife. Diese Schreibweise ist somit semantisch äquivalent mit while(true):
for ( ; ; ) ;
Die trennenden Semikolons dürfen nicht verschwinden. Falls demnach keine Schleifenbedingung angegeben ist, ist der Ausdruck immer wahr. Es folgt keine Initialisierung und keine Auswertung des Fortschaltausdrucks.
Schleifen, und das gilt insbesondere für for-Schleifen, können geschachtelt werden. Syntaktisch ist das auch logisch, da sich innerhalb des Schleifenrumpfs beliebige Anweisungen aufhalten dürfen.
Beispiel Gib fünf Zeilen von Sternchen aus, wobei in jeder Zeile immer ein Stern mehr erscheinen soll. Als besonderes Element ist die Abhängigkeit des Schleifenzählers j von i zu werten.
for( int i = 1; i <= 5; i++ ) { for ( int j = 1; j <= i; j++ ) System.out.print( "*" ); System.out.println(); }Es folgt die Ausgabe: * ** *** **** ***** |
Die übergeordnete Schleife nennt sich äußere Schleife, die untergeordnete innere Schleife. In unserem Beispiel wird die äußere Schleife die Zeilen zählen und die innere die Sternchen in eine Zeile ausgeben, also für die Spalte verantwortlich sein.
Da Schleifen beliebig tief geschachtelt werden können, muss besonders ein Auge auf die Laufzeit geworfen werden. Die inneren Schleifen werden immer so oft ausgeführt, wie die äußere Schleife durchlaufen wird.
Im ersten und letzen Teil einer for-Schleife lässt sich ein Komma einsetzen. Damit lassen sich entweder mehrere Variablen gleichen Typs deklarieren - wie wir es schon kennen - oder mehrere Ausdrücke nebeneinander schreiben.3
Beispiel Schleife mit zwei Zählern:
for ( int i = 1, j = 9; i <= j; i++, j-- ) System.out.println( i + "*" + j + " = " + i*j ); |
|
Dann ist die Ausgabe: 1*9 = 9 2*8 = 16 3*7 = 21 4*6 = 24 5*5 = 25 |
Beispiel Berechne vor dem Schleifendurchlauf den Startwert für die Variablen x und y. Erhöhe dann x und y und führe die Schleife aus, bis x und y beide 10 sind.
int x, y; for ( x = initX(), y = initY(), x++, y++; x == 10 && y == 10; x += xinc(), y += yinc() ) { // ... } |
| Tipp Komplizierte for-Schleifen sind lesbarer, wenn die drei for-Teile in getrennten Zeilen stehen. |
Wird das Komma für die Deklaration mehrerer Variablen verwendet, so kann dahinter kein Ausdruck mit Komma abgetrennt werden. Wenn der Compiler mit einer Deklaration beginnt, könnte er gar nicht zwischen einer zweiten Deklaration für eine Variable und dem folgenden Ausdruck unterscheiden, da das Komma die Variablennamen abtrennt.
Beispiel Folgende for-Schleife ergibt einen Fehler:
int i; for ( int cnt = 0, i = 1; cnt < 10; cnt++ ) ; |
Der erste Teil leitet eine Anweisung ein. Nach dem Komma folgt für den Compiler aber die Deklaration einer zweiten Variablen. Da sie jedoch schon eine Zeile vorher definiert wurde, meldet der Compiler einen Fehler.
Auch umgekehrt funktioniert das nicht, denn eine Variablendeklaration ist kein Ausdruck, sie ist formal betrachtet eine Anweisung.
Beispiel Einer Anweisung kann keine Variablendeklaration folgen. Daher ist auch Folgendes falsch:
for ( i = 0, int j = 0; ; ) ; |
Im letzten Teil von for, dem Fortschaltausdruck, darf keine Variablendeklaration stehen. Wozu sollte das auch gut sein?
Zu den weiteren Einschränkungen gehört, dass es nicht möglich ist, Variablen unterschiedlicher Typen zu deklarieren, wie es etwa for (int i = 0, double d = 0.0;;) zeigt. Hier muss eine Variable außerhalb der for-Schleife definiert werden. Das Deklarieren einer äußeren Variable bringt vielleicht aber auch den unerwünschten Effekt mit sich, dass eine Variable, die eigentlich nur in der Schleife gültig sein soll, eine zu große Sichtbarkeit bekommt. Dann lässt sich ein Block aufspannen, in dem dann nur eine Variable gültig ist.
Beispiel Deklariere ein double und eine Ganzzahl-Variable für die Schleife
{ double d = 12; for ( int i = 0; i < 20; i++ ) ; } // jetzt sind i und d wieder frei double d = 12; |
Wird innerhalb einer for-, while- oder do/while-Schleife eine break-Anweisung eingesetzt, so wird der Schleifendurchlauf beendet.
Beispiel Führe die Schleife so lange durch, bis i den Wert 0 hat
int i = 10; while ( true ) if ( i-- == 0 ) break; |
Die Anweisung ist nützlich, um im Programmblock festzustellen, ob die Schleife noch einmal durchlaufen werden soll. Sie entlastet den Schleifenkopf, der sonst die Bedingung testen würde. Da ein kleines break jedoch im Programmtext verschwinden könnte, die Bedeutung aber groß ist, sollte ein kleiner Hinweis auf diese Anweisung gesetzt werden.
Innerhalb einer for-, while- oder do/while-Schleife lässt sich eine continue-Anweisung einsetzen, die nicht wie break die Schleife beendet, sondern zum Schleifenkopf zurückgeht, sodass dort eine neue Prüfung durchgeführt werden kann, ob die Schleife weiter durchlaufen werden soll. Ein häufiges Einsatzfeld sind Schleifen, die im Rumpf immer wieder Werte so lange holen und testen, bis sie passend zur Weiterverarbeitung sind.
Beispiel Gebe die geraden Zahlen von 0 bis 10 aus
for ( int i = 0; i <= 10; i++ ) { if ( i%2 == 1 ) continue; System.out.println( i + " ist eine gerade Zahl" ); } |

Hier klicken, um das Bild zu Vergrößern
Obwohl das Schlüsselwort goto in der Liste der reservierten Wörter auftaucht, ist diese Operation nicht erlaubt. Programmieren mit goto sollte vermieden werden. Mit dem Konzept von break lässt sich gut leben, und es kann auch noch ruhigen Gewissens eingesetzt werden. Doch zum Schrecken vieler kann break noch schmutziger eingesetzt werden, nämlich mit einer Sprungmarke. Das bringt Java verdächtig nahe in die goto-Welt der unstrukturierten Programmiersprachen, was die Entwickler eigentlich vermeiden wollten. Da jedoch Abbruchbedingungen - der häufigste Einsatzort eines goto - vereinzelt auftreten, wurden in Java break und continue mit Sprungmarken eingeführt.
Beispiel Der Einsatz von break oder continue:
one: while ( condition ) { ... two: while ( condition ) { ... // break oder continue } // nach two } // nach one |
Wird innerhalb der zweiten while-Schleife ein break platziert, dann würde es beim Aufruf die while-Schleife beenden. Das continue würde zur Fortführung der while-Schleife führen. Dieses Verhalten entspräche C, aber in Java ist es erlaubt, hinter den Schlüsselworten break und continue Sprungmarken zu setzen. Das C-Verhalten kann in Java mit break two oder continue two beschrieben werden. Dass aber auch beispielsweise break one möglich ist, zeigt die Mächtigkeit dieses Befehls. Durch break und continue mit Marken ist daher ein goto nahezu überflüssig.
1 Integer.MAX_VALUE + Integer.MAX_VALUE + 2 ist wieder gleich 0. Die Schleife muss also »nur« 4.294.967.296 Mal durchlaufen werden.
2 Im Gegensatz zu C++ ist das Verhalten klar definiert, und es gibt kein hin und her. In C++ implementierten Compilerbauer die Variante einmal so, dass die Variable nur im Block gilt, andere interpretierten die Sprachspezifikation so, dass diese auch außerhalb gültig blieb. Die aktuelle C++-Definition schreibt nun vor, dass die Variable außerhalb des Blocks nicht mehr gültig ist. Da es jedoch noch alten Programmcode gibt, haben viele Compilerbauer eine Option eingebaut, mit der das Verhalten der lokalen Variablen bestimmt werden kann.
3 Wenn Java eine ausdrucksorientierte Sprache wäre, dann könnten wir hier beliebige Programme hineinlegen.
| << zurück |
Copyright (c) Galileo Press GmbH 2004
Für Ihren privaten Gebrauch dürfen Sie die Online-Version natürlich ausdrucken. Ansonsten unterliegt das <openbook> denselben Bestimmungen, wie die gebundene Ausgabe: Das Werk einschließlich aller seiner Teile ist urheberrechtlich geschützt. Alle Rechte vorbehalten einschließlich der Vervielfältigung, Übersetzung, Mikroverfilmung sowie Einspeicherung und Verarbeitung in elektronischen Systemen.