Advanced Java Services | Schleifen (Kontrollstrukturen 2) |
Schleifen dienen dazu, einen Teil des Programms, also eine oder mehrere Anweisungen zu wiederholen.
Diese werden solange wiederholt, bis eine bestimmte Bedingung nicht mehr erfüllt ist. Diese
Bedingung kann man Durchlaufbedingung nennen. Es gibt auch die Möglichkeit, bei Schleifen mit einer
Abbruchbedingung zu arbeiten, dann werden die Anweisungen solange wiederholt, bis diese Abbruchbedingung
eintritt. Und es gibt Sprachen, bei denen bei der einen Schleifenform eine Abbruchbedingung notwendig ist,
die andere Form aber eine Durchlaufbedingung braucht. Das erfordert ein ständiges Umdenken wenn man
sich etwa entschließt, eine Schleife zu ändern. Das bleibt uns erspart:
In C/C++ und Java arbeiten alle Schleifen mit einer Durchlaufbedingung.
Die Anweisungen in der Schleife werden solange wiederholt, solange diese Bedingung erfüllt ist.
Traditionell unterscheidet man kopfgesteuerte und fußgesteuerte Schleifen. Bei kopfgesteuerten
Schleifen wird die Durchlaufbedingung am Anfang geprüft, bei fußgesteuerte Schleifen erst
am Schluß. Logische Konsequenz ist, daß es bei fußgesteuerte Schleifen immer mindestens
einen Durchlauf gibt, es bei kopfgesteuerten Schleifen aber Vorkommen kann, daß sie kein einziges
Mal durchlaufen werden, nämlich dann, wenn die Bedingung bereits am Anfang nicht erfüllt ist.
Aus diesem Grund nennet man kopfgesteuerte Schleifen auch abweisende Schleifen und fußgesteuerte
Schleifen nichtabweisende Schleifen.
kopfgesteuerte Schleife = abweisende Schleife ( evtl. kein Durchlauf )
fußgesteuerte Schleife = nichtabweisende Schleife ( mindestens ein Durchlauf )
Es gibt drei Arten von Schleifen, while-, for-, und do-while-Schleifen. while- und for-Schleifen
sind kopfgesteuert, die do-while-Schleife ist fußgesteuert.
Die while-Schleife hat die einfachste Struktur. Der boolesche Ausdruck stellt die Durchlaufbedingung dar.
while( boolescher_Ausdruck ) anweisung ;
bzw.
while( boolescher_Ausdruck ) { anweisung1 ; anweisung2 ; // beliebig viele Anweisungen }
Als Beispiel nehmen wir eine mathematische "Spielerei", das 3x+1 Spiel. Wir denken uns eine ganze positive
Zahl. Ist sie durch 2 teilbar, so teilen wir sie durch 2, wenn nicht, dann verdreifachen wir sie und
zählen eins dazu. Mit der so erhaltenen Zahl verfahren wir genauso. Man wird feststellen, daß man
nach mehr oder weniger Durchläufen immer bei der Zahl 1 landet. Zumindest ist bis jetzt kein
Gegenbeispiel bekannt. Der Mathematiker Lothar Collatz hat 1937 diese Vermutung in die Welt gebracht
und sie ist noch nicht bewiesen worden
( siehe etwa CollatzProblem.html ) .
Wir lassen den Benutzer eine Zahl eingeben und geben ihm zu dieser Zahl die zugehörige Collatzkette aus.
Wir lassen die Schleife solange laufen, bis sich durch den Algorithmus die 1 einstellt.
System.out.print("Collatz-Kettenanfang : "); int a = Stdin.intEingabe(); while(a != 1) { if ( a%2 == 0 ) // a ist durch 2 teilbar a=a/2 ; else a = 3*a+1 ; System.out.print(a+"\t"); } System.out.println();
Würden Sie eine Zahl finden, bei der sich hier eine Endlosschleife einstellt, dann hätten Sie das Collatzproblem (durch ein Gegenbeispiel) gelöst. Verschwenden Sie nicht zuviel Zeit damit, denn im Wertebereich von int oder long gibt es eine solche Zahl mit Sicherheit nicht. Es bietet sich hier an, den bedingten Ausdruck anzuwenden:
System.out.print("Collatz-Kettenanfang : "); int a = Stdin.intEingabe(); while(a != 1) { a = ( a%2 == 0 ) ? a/2 : 3*a+1 ; System.out.print(a+"\t"); } System.out.println();
Die for-Schleife erscheint in zwei leicht unterschiedlichen Varianten. Die erste Form kommt von C
her, die zweite von C++.
Traditionelle Form (von C herkommend)
for( initialisierungs_Ausdruck ; boolescher_Ausdruck ; reinitialisierungs_Ausdruck) anweisung ;
bzw.
for( initialisierungs_Ausdruck ; boolescher_Ausdruck ; reinitialisierungs_Ausdruck) { anweisung1 ; anweisung2 ; // beliebig viele Anweisungen }
Von C++ herkommende Form
for( Vereinbarung ; boolescher_Ausdruck ; reinitialisierungs_Ausdruck) anweisung;
bzw.
for( Vereinbarung ; boolescher_Ausdruck ; reinitialisierungs_Ausdruck) { anweisung1 ; anweisung2 ; // beliebig viele Anweisungen }
Die runden Klammern werden durch die beiden Strichpunkte in drei Bereiche aufgeteilt. Der einfachsten Fall ist eine klassische Zählschleife, etwa eine, die die Summe der Zahlen von 1 bis 100 bildet:
int i, sum ; for( sum = 0 , i = 0 ; i <= 100 ; i++ ) sum += i ; System.out.println("sum = " + sum);
Als weiteres Beispiel schreiben wir unser 3x+1 Spiel mit einer for-Schleife.
int a ; System.out.print("Collatz-Kettenanfang : "); for( a = Stdin.intEingabe() ; a != 1 ; ) { a = ( a%2 == 0 ) ? a/2 : 3*a+1 ; System.out.print(a+"\t"); }
Im obigen verwenden wir die ältere Form der for-Schleife. Hier nochmal unser 3x+1 Problem mit der neueren Form der for-Schleife:
System.out.print("Collatz-Kettenanfang : "); for(int a = Stdin.intEingabe() ; a != 1 ; a = ( a%2 == 0 ) ? a/2 : 3*a+1 ) System.out.print(a+"\t");
Außerdem haben wir hier die bedingte Anweisung im dritten Bereich der for-Schleife untergebracht.
So sparen wir uns die Blockklammern.
Bei der zweiten for-Schleife haben wir den dritten Bereich leer gelassen. Jeder der drei Bereiche kann
leer bleiben. Falls der Bereich mit dem booleschen Ausdruck leer bleibt, wird dieser Bereich auf true
gesetzt, for( ; true ; ) ist also gleichbedeutend
mit for( ; ; ) .
Die meisten Programmierer bevorzugen die for- oder while- Schleife. Die do-while Schleife wird seltener benützt.
do anweisung ; while( boolescher_Ausdruck ) ;
bzw.
do { anweisung1 ; anweisung2 ; // beliebig viele Anweisungen } while( boolescher_Ausdruck ) ;
Nochmal unser 3x+1 Beispiel:
System.out.print("Collatz-Kettenanfang : "); int a = Stdin.intEingabe(); do { a = ( a%2 == 0 ) ? a/2 : 3*a+1 ; System.out.print(a+"\t"); } while(a != 1) ;
Da wir hier eine nichtabweisende Schleife haben, läuft die Schleife auch für a = 1. Wegen 1 -> 4 -> 2 -> 1 stoppt sie aber trotzdem.
Ab der Version 1.5 von Java gibt es eine weitere Form der for-Schleife für die sich der Name for each Schleife eingebürgert hat. Diese Form der Schleife vereinfacht den Kopf der for-Schleife auf elegante Weise. In Java wurde für diese neue Form kein neues Schlüsselwort (each) notwendig. Die for each Schleife kann jedoch nur für Arrays und Erweiterungen des Arraykonzepts verwendet werden. Aus diesem Grund besprechen wir diese Schleife erst im übernächsten Kapitel im Anschluß an das Kapitel über Arrays. Wer sofort Informationen zu dieser Form der Schleife haben will kann den folgenden Link verwenden: for each.
Die Wirkungsweise von break ist sehr einfach zu verstehen. Ein Aufruf von break beendet eine Schleife sofort. Mit continue dagegen kann man an das Ende eines Schleifenrumpfes springen. Alle Anweisungen ab continue werden dann übersprungen und die Verarbeitung geht mit dem nächsten Schleifendurchlauf weiter. Das Verhalten einer Schleife mit break und continue kann man sehr gut an dem folgenden Beispiel studieren.
Random rd = new Random(); for( int i ; ; ) { System.out.println("begin"); i = rd.nextInt(256); System.out.println(i); if(i%3==0) { System.out.println("durch 3 teilbar, continue"); continue; } if(i%3==1) { System.out.println("Rest 1, break"); break; } if(i%3==2) { System.out.println("Rest 2, normal weiter"); } System.out.println("end"); }
Einige Summen
Die Folge von Fibonacci
kgV und ggT