Kapitel 5 Programmierkurs Kontrollstrukturen Die if-Anweisung Die switch-Anweisung Die for-Schleife Die while-Schleife Die do-Schleife Birgit Engels, Anna Schulze ZAIK Universität zu Köln WS 07/08 1 / 55 Kontrollstrukturen 2 Arten von Kontrollstrukturen Bis jetzt wurden in jedem Programm alle Befehle nacheinander einmal ausgeführt: Das Programm machte (bis auf unterschiedliche Eingabewerte) immer das gleiche. Bedingte Ausführung ◮ ◮ if/if ... else-Anweisung switch-Anweisung (Bedingte) Mehrfachausführung Sogenannte “Kontrollstrukturen” sind besondere Anweisungen, die es einem Programm ermöglichen, sich unterschiedlich zu verhalten oder Befehlsabfolgen mehrfach auszuführen. ◮ ◮ ◮ 3 / 55 for-Schleife while-Schleife do ... while-Schleife 4 5.1 Die if-Anweisung Beispiel für if: IfElse.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. if ( < Bedingung > ) < Anweisung > oder if ( < Bedingung > ) < Anweisung 1 > else < Anweisung 2 > < Bedingung > ist ein Wert ∈ {true, false}, eine Variable vom Typ boolean oder ein boolscher Ausdruck, der zu einem solchen Wert ausgewertet wird. Anweisung ist eine einzelne Anweisung oder ein Block von Anweisungen. Im ersten Fall wird die Anweisung nur ausgeführt, wenn die Bedingung den Wert true hat. Im zweiten Fall wird die Anweisung1 ausgeführt, wenn die Bedingung den Wert true hat. Andernfalls wird Anweisung2 ausgeführt. public static void main(String[] args) { boolean b=true, d=false; int a=1, c=1; if (b) b=false; if (!b) System.out.println(‘‘b=false’’); if (a>c) System.out.println(‘‘a>c’’); else System.out.println(‘‘a<c oder a=c’’); if (a==c) System.out.println(‘‘a=c’’); if (b=d) System.out.println(‘‘b=d’’); } b=false a<c oder a=c a=c 5 / 55 Beispiel für if: IfElse.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Beispiel für if: IfElse.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. public static void main(String[] args) { boolean b=true, d=false; int a=1, c=1; if (b) b=false; if (!b) System.out.println(‘‘b=false’’); if (a>c) System.out.println(‘‘a>c’’); else System.out.println(‘‘a<c oder a=c’’); if (a==c) System.out.println(‘‘a=c’’); if (b=d) System.out.println(‘‘b=d’’); } b=false a<c oder a=c a=c 6 Bedingung liefert Wert true: Anweisung wird ausgefürt. 7 / 55 public static void main(String[] args) { boolean b=true, d=false; Auswertung von “a>b” liefert Wer int a=1, c=1; false: Anweisung wird nicht aus if (b) b=false; gefürt. if (!b) System.out.println(‘‘b=false’’); if (a>c) System.out.println(‘‘a>c’’); else System.out.println(‘‘a<c oder a=c’’); if (a==c) System.out.println(‘‘a=c’’); if (b=d) System.out.println(‘‘b=d’’); } b=false a<c oder a=c a=c Statt dessen wir die else-Anweisung ausgefürt. 8 Beispiel für if: IfElse.java Beispiel 2 für if: BlockIfElse.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. public static void main(String[] args) { boolean b=true, d=false; int a=1, c=1; if (b) b=false; if (!b) System.out.println(‘‘b=false’’); if (a>c) System.out.println(‘‘a>c’’); else System.out.println(‘‘a<c oder a=c’’); if (a==c) System.out.println(‘‘a=c’’); if (b=d) System.out.println(‘‘b=d’’); Fehler: Anstelle einer Bedingung ei} ne Zuweisung. Geht nur bei boolschen Werten, da Wert der Zuweisung gelieb=false fert wird. Wert ist false: Anweisung a<c oder a=c wird nicht ausgefürt. a=c 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. public static void main(String[] args) { int a=3, b=4, min, max; if (a<b) { min=a; max=b; } else { min=b; max=a; } System.out.println(‘‘min= ’’+min+‘‘ max= ’’+max); } min= 3 max= 4 9 / 55 Beispiel 2 für if: BlockIfElse.java 10 Häufige Fehler bei if 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. public static void main(String[] args) { int a=3, b=4, min, max; if (a<b) { min=a; max=b; } else { min=b; max=a; } Zuweisungen von min und max wird System.out.println(‘‘min= ’’+min+‘‘ max= ’’+max); durch Block zu einer Anweisung. In} nerhalb solcher Blöcke können auch weitere if-Anweisungen auftreten. min= 3 max= 4 11 / 55 if (a<b) { min=a; max=b; } else min=b; max=a; if (a<b); min=a; Kein Block im else-Teil : Als else-Anweisung gilt nur min=b;: max=a; wird immer ausgeführt (vgl. NoBlockIfElse.java). Abschluss nach if-Bedingung mit ;: leere if-Anweisung und min=a; wird immer ausgeführt. 12 Beispiel 3 für if: MoreIfElse.java 1. 2. 3. 4. 5. 6. 7. Beispiel 3 für if: MoreIfElse.java 1. 2. 3. 4. 5. 6. 7. public static void main(String[] args) { int a=1, b=2; if (a<b) System.out.println(‘‘a<b’’); else if (a>b) System.out.println(‘‘a>b’’); else System.out.println(‘‘a=b’’); Verschachtelte if- Anweisung mit } else if: Z.B. als Fallunterscheidung. a<b public static void main(String[] args) { int a=1, b=2; if (a<b) System.out.println(‘‘a<b’’); else if (a>b) System.out.println(‘‘a>b’’); else System.out.println(‘‘a=b’’); } a<b 13 / 55 Fallunterscheidungen mit else if 14 5.2 Die switch-Anweisung switch (< Ausdruck >) { case < Wert1 > : < Anweisung >; break; case < Wert2 > : < Anweisung >; break; ... default : < Anweisung >; break; } char c=’x’; int ascii; if (c==’a’) ascii=97; else if (c==’b’) ascii=98; ... else if (c==’z’) ascii=122; else ascii=0; < Ausdruck >: arithmetischer Ausdruck oder einzelne Variable. Fallunterscheidungen mit else if sind bei vielen Fällen unkomfortabel und enthalten den Overhead der Vergleichsbedingung. Für solche Anwendungen verwendet man daher die switch- Anweisung. < Wert >: Wert, den < Ausdruck > annehmen kann. < Anweisung >: einzelne Anweisungen Block von Anweisungen. Die Anweisungen hinter default werden ausgeführt, falls < Ausdruck > keinen der < Wert >e annimt. 15 / 55 16 switch vs. else if Beispiel für switch: AsciiSwitch.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. Die Zeile case < Wert > : < Anweisung >; break; einer switch-Anweisung entspricht in etwa der Zeile: (else) if (< Ausdruck >==< Wert >) < Anweisung >; einer else if-Anweisung. Die Zeile default : < Anweisung >; break; einer switch-Anweisung entspricht der letzten Zeile: else < Anweisung >; // einer else if-Anweisung. public static void main(String[] args) { char c=’e’; int ascii; switch (c) { case ’a’: ascii=97; break; case ’b’: ascii=98; break; ... case ’z’: ascii=122; break; default: ascii=0; break; } System.out.println(ascii); } Nur im default-Fall darf break; weggelassen werden. 101 17 / 55 Beispiel für switch: AsciiSwitch.java 18 Beispiel für switch: AsciiSwitch.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. public static void main(String[] args) { char c=’e’; int ascii; switch (c) { case ’a’: ascii=97; break; case ’b’: ascii=98; break; ... case ’z’: ascii=122; break; default: ascii=0; break; } Wichtig: Erst break; schliesst einen System.out.println(ascii); case ab. Ohne break; werden weite} re Anweisungen ausgeführt! 101 19 / 55 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. ... switch (c) { case ’a’: ascii=97; break; ... case ’e’: ascii=101; case ’f’: ascii=102; break; ... case ’z’: ascii=122; break; default: ascii=0; break; } ... 102 20 Beispiel für switch: AsciiSwitch.java 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. Bemerkung zu break ... switch (c) { case ’a’: ascii=97; break; ... case ’e’: ascii=101; case ’f’: ascii=102; break; ... case ’z’: ascii=122; break; default: ascii=0; break; } Vergessenes break;: Nächste Anwei... sungen ausgeführt - Falscher Wert! Es kann natürlich auch sinnvoll sein, einen Fall einer switch-Anweisung nicht mit break; abzuschliessen. Z.B.: Wenn in 2 Fällen die gleiche Anweisung folgen soll oder im ersten Fall nur zusätzliche Anweisungen zuvor auszuführen sind. Da ein weggelassenes break nur der Bequemlichkeit dient, ein vergessenes aber zu Fehlern führt, sollte generell jedes case mit einem break; abgeschlossen werden. 102 21 / 55 5.3 Die for-Schleife 22 Verwendung der for-Schleife Die for-Schleife wird meist verwendet, um eine Folge von Anweisungen (Block) mehrfach auszuführen. Die Zahl der Ausführungen ist dabei oft vorher klar (feste Zahl oder begrenzt durch Wert einer Variablen). Daher werden for-Schleifen auch Zählschleifen genannt. Dabei werden immer folgende Schritte for (< Initialbefehl >; < Schleifenbedingung >; < Iteration > ) < Schleifenanweisung >; < Initialbefehl >: Einmalig durchgeführte Anweisung. Meist Deklaration+Initialisierung der Iterationsvariablen (Schleifenindex). ausgeführt: 1. Initiatbefehl wird einmal ausgeführt. 2. Bedingung wird geprüft: < Schleifenbedingung >: Bedingung, die für die nächste Ausführung der < Schleifenanweisung > erfüllt sein muss. ◮ ◮ < Iteration >: Hochzählen des Schleifenindex. Falls true: Anweisungen werden ausgeführt. Falls false: Schleife wird beendet. 3. Iteration wird ausgeführt. < Schleifenanweisung >: Einzelne Anweisung oder Block von Anweisungen. 4. Weiter bei 2. Häufigste Art der Verwendung: for( int i=0; i<xyz; i++) {tuwas;} 23 / 55 24 Beispiel für for-Schleife (ForIter.java) 1. 2. 3. 4. 5. 6. Beispiel für for-Schleife (ForIter.java) 1. 2. 3. 4. 5. 6. public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Der Anfangsbefehl wird genau einmal ausgeführt. i erhält einmalig den Wert 0. 0 1 2 0 1 2 25 / 55 Beispiel für for-Schleife (ForIter.java) 26 Beispiel für for-Schleife (ForIter.java) 1. 2. 3. 4. 5. 6. 1. 2. 3. 4. 5. 6. public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Die Bedingung i<anzahl wird geprüft. Für i=0 und anzahl=3 wird der 0 Wert true zurückgegeben. 1 2 public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Also wird der Anweisungsblock wird ausgeführt. 0 Es wird 0 auf dem Bildschirm ausgegeben. 1 2 27 / 55 28 Beispiel für for-Schleife (ForIter.java) Beispiel für for-Schleife (ForIter.java) 1. 2. 3. 4. 5. 6. 1. 2. 3. 4. 5. 6. public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Der Iterationsbefehl i++ wird ausgeführt. i hat nun den Wert 1. 0 1 2 public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Die Bedingung i<anzahl wird geprüft. Für i=1 und i=2 wird wieder 0 der Wert true zurückgegeben. 1 2 29 / 55 Beispiel für for-Schleife (ForIter.java) 30 Beispiel für for-Schleife (ForIter.java) 1. 2. 3. 4. 5. 6. 1. 2. 3. 4. 5. 6. public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Der Anweisungsblock wird ausgeführt. Es wird 1 und 2 auf dem Bildschirm aus0 gegeben 1 2 public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Der Iterationsbefehl i++ wird ausgeführt. i hat nun den Wert 3. 0 1 2 31 / 55 32 Beispiel für for-Schleife (ForIter.java) Fehlerquellen: Die Schleifenanweisung kann (wie bei if) auch (versehentlich) die leere Anweisung sein: for( int i=0; i<xyz; i++); {tuwas;} Hier wird tuwas; nur einmal, nach der Schleife ausgeführt. 1. 2. 3. 4. 5. 6. public static void main(String[] args) { int anzahl=3; for ( int i=0; i<anzahl; i++) System.out.println(i); } Die Bedingung i<anzahl wird geprüft. Für i=3 und anzahl=3 wird der 0 Wert false zurückgegeben. 1 Die Schleife wird beendet. 2 Ist die Fortsetzungsbedingung am Anfang falsch, so wird die Schleifenanweisung nie ausgeführt: int xyz=0; for( int i=0; i<xyz; i++) {tuwas;} Hier wird tuwas; wegen i<xyz = false nie ausgeführt. Die Anzahl der Schleifendurchläufe wird oft falsch bestimmt, insbesondere durch folgende Unterschiede: i<xyz; i<=xyz; int i=0; int i=1; 33 / 55 Sonderfälle 34 Allgemeine Fälle for-Schleifen können natürlich geschachtelt werden: for(int i=1; i<n; i++) { for(int j=1; j<m; j++) { System.out.println(i*j); } } Bisher wurde Schleifenindex im Anfangsbefehl deklariert und war nur in der Schleife bekannt. Deklaration ist auch vor der Schleife möglich. Man kann dann auch nach Ende der Schleife darauf zugreifen: int i; for(i=0; i<n; i++) {tuwas;} System.out.println(i); Die Iteration wird einmal nach jeder Schleifenanweisung ausgeführt. Nach Ende der Schleife hat der Index den Wert, für den die Fortsetzungsbedingung erstmals falsch war. Obiges Programmfragment liefert also die folgende Ausgabe: Bei einzelnen Anweisungen oder einem Anweisungsblock in nur der innersten Schleife, kann die Schreibweise verkürzt werden: for(int i=1; i<n; i++) for(int j=1; j<m; j++) System.out.println(i*j); 10 Bei geschachtelten Schleifen auf unterschiedliche Schleifenindizes achten! Achtung: Für Bedingung i<=10, ergibt sich die Ausgabe 11! 35 / 55 36 Beispiel 2 für for-Schleife (ForFakul.java) Beispiel 2 für for-Schleife (ForFakul.java) 1. public static void main(String[] args) 2. { 3. int f=1; 4. for (int i=5; i>0; i--) 5. f*=i; 6. System.out.println(f); 7. } 1. public static void main(String[] args) 2. { 3. int f=1; 4. for (int i=5; i>0; i--) 5. f*=i; 6. System.out.println(f); 7. } Die Variable i kann auch dekrementiert, statt in120 krementiert werden! 120 37 / 55 Beispiel 2 für for-Schleife (ForFakul.java) 38 Unbestimmte Schleifen 1. public static void main(String[] args) 2. { 3. int f=1; 4. for (int i=5; i>0; i--) 5. f*=i; 6. System.out.println(f); 7. } Dann muss die Variable i nach unten begrenzt 120 werden! Einzelne Elemente der for-Klammer können leer bleiben. Es funktioniert auch: for( ; ; ) {tuwas;} Dabei ist eine leere Bedingung immer wahr, d.h. hier wird tuwas; unendlich oft ausgeführt. Trotz leerer Bedingung können die Schleifendurchläufe gezählt werden: for(int i=1; ; i++ ) {tuwas;} 39 / 55 40 Verlassen unbestimmter Schleifen break und continue Unbestimmte Schleifen können auf 2 Arten verlassen werden: 1. Es tritt eine “Ausnahme” (Exception) auf (später). Mit break wird eine Schleife komplett abgebrochen, falls die vorhergehende Bedingung erfüllt ist. 2. Die Schleife wird abhängig von einer Bedingung mit break verlassen: for(int i=1; ; i++ ) { if (i>10) break; } Eventuell soll nur ein bestimmter Fall übersprungen werden, für den die Schleife nicht durchgeführt werden soll. Einmaliges überspringen des restlichen Schleifenkörpers gelingt mit: if (i==10) continue; Ist die Bedingung erfüllt springt das Programm direkt zum Ende des Durchlaufs und startet die Schleife mit dem nächsten Durchlauf. Beide Arten eine unbestimmte Schleife zu verlassen sind sehr unschön, da sie zu unübersichtlichen, schwer zu verifizierenden Programmen führen. Daher sollten unbestimmte Schleifen immer vermieden werden. Dies ist (manchmal aufwendiger, aber) immer möglich! Eine Programmiersprache ist mit break nicht mächtiger als ohne! 41 / 55 Beispiel für continue 42 5.4 Die while-Schleife for(int i=1; i<20 ; i++ ) { if (i==10) continue; System.out.println(i); } while (< Schleifenbedingung >) < Schleifenanweisung >; < Schleifenbedingung >: Bedingung, die für die nächste Ausführung der < Schleifenanweisung > erfüllt sein muss. Dieses Programmfragment gibt alle Werte von 1 bis 19 aus, ausser der 10. Das gleiche ist aber leicht ohne continue möglich: < Schleifenanweisung >: Einzelne Anweisung oder Block von Anweisungen, die ausgeführt werden, falls < Schleifenbedingung > true liefert. for(int i=1; i<20 ; i++ ) { if (i!=10) System.out.println(i); } 43 / 55 44 Verwendung der while-Schleife Beispiel für while-Schleife (WhileDo.java) 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. while (zahl>1) 5. { 6. zahl/=2; 7. if (zahl>0) i++; 8. } 9. System.out.println(i); 10. } Im Gegesatz zur for-Schleife steht bei der while-Schleife die Schleifenbedingung, nicht die Anzahl der Schleifendurchläufe eher im Vordergrund. Um die Anzahl zu ermitteln, muss man selbst einen Zählindex verwalten. Die Schleifenbedingung ist ein eher komplexerer boolescher Ausdruck. Es werden immer folgende Schritte ausgeführt: 1. Bedingung wird geprüft: ◮ ◮ Falls true: Anweisungen werden ausgeführt. Falls false: Schleife wird beendet. 2. Weiter bei 1. 6 45 / 55 Beispiel für while-Schleife (WhileDo.java) 46 Beispiel für while-Schleife (WhileDo.java) 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. while (zahl>1) 5. { 6. zahl/=2; 7. if (zahl>0) i++; 8. } 9. System.out.println(i); 10. } Die Bedingung zahl>1 wird geprüft. Für zahl=64 wird der Wert true zurückge6 geben. 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. while (zahl>1) 5. { 6. zahl/=2; 7. if (zahl>0) i++; 8. } 9. System.out.println(i); 10. } Der Anweisungsblock wird ausgeführt. zahl wird ihre ganzzahlige Hälfte zugewie6 sen; da diese > 0 ist, wird i inkremeniert. 47 / 55 48 Beispiel für while-Schleife (WhileDo.java) Beispiel für while-Schleife (WhileDo.java) 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. while (zahl>1) 5. { 6. zahl/=2; 7. if (zahl>0) i++; 8. } 9. System.out.println(i); 10. } Die Bedingung wird erneut (nun mit zahl=32) geprüft, usw... 6 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. while (zahl>1) 5. { 6. zahl/=2; 7. if (zahl>0) i++; 8. } 9. System.out.println(i); 10. } Es wird der Exponent der grössten enthaltenen Zweierpotenz ausgegeben. 6 49 / 55 Sonderfälle for vs. while Eine leere Bedingung, ist bei while-Schleifen anders als bei for-Schleifen standardmässig nicht vorgesehen (erzeugt Kompilierfehler). Eine Endlosschleife mittels while wird mit true als Bedingung erreicht: while (true) < Schleifenanweisung >; Es gelten die gleichen Möglichkeiten zum verlassen einer solchen Endlosschleife mit while wie mit for: ◮ ◮ 50 Die while-Schleife und for-Schleife sind äquivalent, es gilt: for(Start; Bedingung; Änderung) Anweisung; entspricht: Start; while (Bedingung) { Anweisung; Änderung; } Exceptions break; Auch hier kann continue; zum Überspringen einer Ausführung verwendet werden. while-Schleifen können verschachelt werden (Blöcke bilden!). 51 / 55 52 Bemerkung 5.5 Die do-Schleife do < Schleifenanweisung > while (< Schleifenbedingung >); Wie in den Beispielen gesehen, werden die Bedingungen bei for-Schleife und while-Schleife vor dem Durchlauf geprüft. < Schleifenanweisung >: Einzelne Anweisung oder Block von Anweisungen, die ausgeführt werden. Manchmal ist es wünschenswert, mindestens einen Durchlauf zu erlauben, bevor die Bedingung zum ersten Mal überprüft wird. < Schleifenbedingung >: Bedingung, die für die nächste Ausführung der < Schleifenanweisung > erfüllt sein muss. Daher : do-Schleife. In do-Schleife entspricht der while-Schleife die allen Eigenschaften ausser dem Zeitpunkt der Bedingungsüberprüfung. 53 / 55 Beispiel für do-Schleife (DoWhile.java) 1. public static void main(String[] args) 2. { 3. int zahl=64, i=0; 4. do 5. { 6. if (zahl>0) zahl/=2; 7. i++; 8. } 9. while (zahl>1) 10. System.out.println(i); 11. } 6 55 / 55 54