Schleifen

Eine Schleife braucht man, sobald ein Programm dieselben Arbeitsschritte mehrfach durchführen soll – so lässt sich die enorme Rechengeschwindigkeit von Computern sinnvoll nutzen.

MATERIALIEN

 

Das Programm folgt seiner Anleitung Schritt für Schritt. Manchmal ist es aber wünschenswert, diese lineare Arbeitsweise aufzubrechen.

Einsatz von Schleifen

Kommen wir noch einmal zurück zum minimalistischen Szenario ausgabe_in_konsole.zip (öffnen Sie es, speichern Sie ggf. eine Kopie des momentanen Zustands, dann löschen Sie allen Code, den Sie bisher hineingeschrieben haben).

Sie haben ja bereits herausgefunden, wie man mithilfe einer Instanzvariablen – also einer Variablen, die in der grünen Klassenklammer, nicht aber in einer der gelben Methodenklammern deklariert wird –  die Ausgabe einer fortlaufenden Zahlenreihe programmiert. Bis jetzt war es aber nötig, für jede Ausgabe einmal auf die Act-Schaltfläche zu drücken.

Jetzt hätten wir gerne, dass ein einziger Aufruf der act()-Methode eine Zahl gleich mehrfach ausgibt, z.B.:

7 7 7 7 7 7 7 7 7 7

Wie können wir also dem Computer sagen, dass er mehrmals dasselbe tun soll, z.B. den Befehl System.out.print(meineZahl + " "); ausführen?

1. Die for-Schleife

Die Antwort: mit einer Schleife.

Genau genommen gibt es mehrere Arten von Schleifen. Wenn ich bereits im voraus weiss, wie oft ich etwas machen will, dann ist die for-Schleife am praktischsten. 

For-Schleife

Erstellt von Seraina Hohl

Eine For-Schleife führt einen Code eine (meist) vorbestimmte Anzahl mal aus: Nach jedem Durchlauf wird die Laufvariable gemäss einer anfangs festgelegten Regel verändert und danach überprüft ob ein weiterer Durchlauf stattfinden soll.

Analogie

Für jede Parkplatznummer (hier von 80 bis und mit 91): Kontrolliere, ob ein Parkschein gelöst wurde. Wenn nein, stelle eine Busse aus.

Erstellt von Seraina Hohl

In Java formuliert man das so:

for (int i = 1; i<=10; i++){  //zähle i von 1 bis 10
    //hier steht der Code, der 10 mal ausgeführt werden soll
}

Schauen wir uns das etwas genauer an: Ähnlich wie bei Verzweigungen steht der (hier: zu wiederholende) Code in einem eigenen Block, also in geschweiften Klammern. Davor werden die Bedingungen für das Wiederholen festgelegt: for gibt die Art der Schleife an und muss gefolgt werden von drei Argumenten, eingeschlossen in normale Klammern und getrennt von Semikolons:

  • An der ersten Stelle wird die Zählvariable initialisiert, hier benutzen wir eine Ganzzahl i, die anfangs auf den Wert 1 gesetzt wird.
  • An der zweiten Stelle steht die Laufbedingung, ähnlich wie bei Verzweigungen muss hier ein Wahrheitswert (also true oder false) herauskommen. Im Beispiel steht hier i<=10. Das bedeutet: Solange der Wert der Zählvariable i kleinergleich 10 ist, wird der Codeblock (erneut) ausgeführt.
  • An der dritten Stelle steht eine Anweisung, die nach jeder Wiederholung der Schleife einmal ausgeführt wird. Der Eintrag im Beispiel (i++) ist eine Kurzform von i = i + 1 (das würde ebenfalls funktionieren), es wird also nach jedem Durchgang der Wert der Zählvariablen i um 1 erhöht.

Auf diese Weise haben wir den Computer angewiesen, den Codeblock zehnmal auszuführen:
Beim ersten Durchgang ist i = 1; wird aber dann auf 2 gesetzt, bevor der Code erneut ausgeführt wird, beim dritten Durchgang haben wir dann schon eine 3, … , und wenn i schliesslich nicht mehr kleinergleich 10 ist (also nach dem zehnten Durchgang), dann wird der Code in den geschweiften Klammern nicht erneut ausgeführt, sondern es geht unterhalb der schliessenden geschweiften Klammer weiter.

Um die obige Ausgabe zu erreichen, müssten wir also folgendes in die act()-Methode schreiben:

for (int i = 1; i<=10; i++){
    System.out.print(7 + " "); //gibt eine 7 und ein Leerzeichen aus
}
System.out.println(" ");  //damit die nächste Ausgabe in eine neue Zeile kommt

Probieren Sie das doch gleich mal aus.

Wenn Sie das obige Beispiel mit ihrem Wissen über Instanzvariablen kombinieren, dann sollten Sie auch ganz leicht zu folgender Ausgabe kommen (eine Zeile pro act()-Aufruf):

1 1 1 1 1 1 1 1 1 1
2 2 2 2 2 2 2 2 2 2
3 3 3 3 3 3 3 3 3 3
....

In der folgenden Variante wird die Zählvariable gleich noch innerhalb des Code-Blocks benutzt, das ist oft sehr praktisch. 

for (int i = 1; i<=10; i++){
    System.out.print(i + " ");
}

Überlegen Sie zuerst, wie die Ausgabe aussehen wird, danach probieren Sie es aus.

Und wie sieht es mit dieser Variante aus?

for (int i = 10; i>0; i=i-2){
    System.out.print(i + " ");
}

Wie unterscheiden sich die Ausgaben der beiden folgenden Codes?

Code 1:

for (int i = 4; i<=20; i=i+4){
    System.out.print(i + " ");
}

Code 2:

for (int i = 1; i<=5; i++){
    System.out.print(i*4 + " ");
}

Sie merken: Oft ist es einfacher, von 0 (oder 1) bis irgenwohin durchzuzählen und dann im Codeblock etwas zu rechnen, anstatt sich eine kompliziertere Initialisierung und Aktualisierung der Variablen auszudenken.

2. Die while-Schleife

While-Schleife

Erstellt von Seraina Hohl

Eine While-Schleife kontrolliert die Laufbedingung und läuft solange, bis sie nicht mehr erfüllt ist. Dann führt sie einen Code (erneut) aus.

Analogie

Prüfe das Brett auf seine Dicke. Ist das Brett zu dick?
Dann fahren wir einmal mit dem Hobel drüber und prüfen danach erneut.

Erstellt von Seraina Hohl

Eine weitere Art von Schleife, die häufig benutzt wird, ist die while-Schleife. Hier ist die Anzahl der Wiederholungen nicht im vorhinein festgelegt, sondern der Code wird so lange immer wieder abgearbeitet, bis eine Bedingung (wie bei der Verzweigung) false ergibt. Besonders nützlich ist das, wenn man weiss, wann die Wiederholung aufhören soll, aber zu bequem ist auszurechnen, wie viele Repetitionen es dafür braucht, z.B.:

int meineZahl = 3;
while (meineZahl < 10000){
    System.out.println(meineZahl);
    meineZahl = meineZahl * meineZahl;
}

Wie Sie hier vielleicht schon sehen, lassen sich die meisten for-Schleifen auch als while-Schleifen formulieren und umgekehrt, meist kann man sich also aussuchen, mit welcher Art man besser zurecht kommt. Das allererste Beispiel in diesem Tutorial sähe als while-Schleife bspw. so aus:

int i = 1; // hier muss man die Zählvariable selbst kreieren und verwalten
while (i <= 10){
    System.out.print(i + " ");
    i++;  //wieder die Kurzform, das Hochzählen darf man nicht vergessen!
}

Wenn Sie die Steuerung der while-Schleife (Zeilen 1, 2 und 4) vergleichen mit den drei Argumenten, die man bei der for-Schleife braucht, dann sehen Sie, dass die for-Schleife nur scheinbar komplizierter ist. Oft ist sie sogar die bessere Wahl, weil man bei der while-Schleife gerne etwas (z.B. das Inkrementieren, i++) vergisst.

Da die Syntax von Schleifen am Anfang etwas gewöhnungsbedürftig ist, passieren hier oft kleine Fehler. Einige der üblichsten finden Sie bei den Anfängerfehlern. Vielleicht hilft Ihnen diese Auflistung, dieselben Fehler zu vermeiden.

Aufgabe

Bearbeiten Sie im Aufgabenblatt Greenfoot_Schleifen.pdf die Aufgaben zur Konsolenausgabe.

3. Turtle Joe

Nun wird es aber wirklich Zeit, dass wir uns mit etwas anderem als Konsolenausgaben beschäftigen. Dafür gibt es ein neues Szenario namens Joe.zip – wie üblich müssen Sie es herunterladen, entpacken, und in Greenfoot öffnen.

Quelle: Oinf

In diesem Szenario gibt es einen Actor namens Turtle (Schildkröte), und Ihre Aufgabe wird es sein, eine spezielle Turtle mit Namen Joe so zu programmieren, dass er/sie/es bestimmte Figuren zeichnet.

Da Sie jetzt nicht mehr mit Zahlen und Buchstaben, sondern mit Joe arbeiten wollen, müssen Sie zunächst herausfinden, was Joe eigentlich kann – also welche Befehle Sie ihm geben können. Joe funktioniert im allgemeinen ähnlich wie der Käfer (Kara), den Sie ja schon ein wenig kennen, aber es gibt doch ein paar Unterschiede. Woher also weiss man, wie man Joe benutzt?

Grundsätzlich gilt: jede Klasse hat die Fähigkeiten ihrer Oberklassen – die Klassen sieht man im obigen Bild in den Rechtecken rechts, die Hierarchie ist mit Pfeilen dargestellt. Also: eine Turtle ist ein spezieller Actor, sie kann alles, was ein Actor kann und hat noch dazu ein paar eigene Fähigkeiten (= Methoden). Joe ist eine spezielle Turtle, er erbt alle Fähigkeiten sowohl von Turtle als auch von Actor – und vielleicht verleihen Sie ihm dazu noch ein paar eigene, z.B. indem Sie seine act()-Methode verändern.

Will man herausbekommen, welche Fähigkeiten vorgegeben sind, sollte man sich die Dokumentation der jeweiligen Klasse ansehen (z.B. Rechts-Klick auf Actor à Open Documentation). Die für die folgenden Aufgaben wichtigsten Methoden finden Sie hier zusammengestellt:

Gegeben ist eine Klasse Turtle, die u.a. folgende Methoden zur Verfügung stellt:

Quelle: OInf.ch
Screenshot von Greenfoot-API

Als Farbangaben können verwendet werden: red, black, blue, yellow, green, magenta, white, gray

In der Actor-Klasse sind u.a. die folgenden praktischen Methoden definiert:

Quelle: OInf.ch
Screenshot von Greenfoot-API

Sie sehen, dass nach dem Methodennamen (z.B. move) in Klammern ein oder mehrere Variablentypen und –namen angegeben sind (z.B. int distance). Den Namen (plus die Klammern) braucht es immer, um eine Methode aufzurufen, also auszuführen. Wenn in den Klammern noch etwas steht, dann heisst das, diese Methode braucht noch zusätzliche Informationen, um zu funktionieren; z.B. muss move() noch wissen, wie weit die Bewegung gehen soll (distance) und diese Distanzangabe muss ausserdem eine int, also ein ganzzahliger Wert sein. Um die Methode zu benutzen, muss man also lediglich dafür sorgen, dass in den Klammern ein ganzzahliger Wert steht, dann erfolgt eine Bewegung über die entsprechende Distanz. Wenn man also will, dass Joe sich um 10 Pixel geradeaus bewegt, dann braucht man den Befehl move(10); wenn er gleichzeitig noch einen Strich zeichnen soll, benutzt man moveAndDraw(10); mit turn(90); bringt man Joe dazu, sich um 90 Grad (im Uhrzeigersinn) zu drehen. Anstatt den Wert direkt in die Klammern zu schreiben kann man natürlich auch eine Variable angeben, die einen Wert des entsprechenden Typs (hier: int) enthält.

Wie Sie oben sehen, gibt es auch noch eine Methode setColor(), die Informationen in Form eines Strings erwartet. Mit dem Befehl setColor("red"); kann ich also dafür sorgen, dass Joe in Zukunft rote Striche zeichnet – zumindest so lange, bis ich die Farbe wieder ändere.

Wenn ich keinen Wert übergebe, obwohl einer erwartet wird (z.B. move();) oder wenn der übergebene Wert einen falschen Typ hat (z.B. move("ABC");), dann bekomme ich einen Fehler beim Kompilieren – was ja auch Sinn macht.

Ein einfaches Beispiel; etwas sehr Ähnliches haben Sie im Kara-Szenario ja auch schon programmiert:

public void act() //das ist die act()-Methode der Klasse Joe
{
    for (int i=0; i<4; i++){
        if (i%2==0){     //wenn i gerade ist
            setColor("blue");
        } 
        else {
            setColor("red");
        }
        moveAndDraw(100);
        turn(90);
    }
}

Überlegen Sie zunächst, was dieser Code wohl bewirken könnte – probieren Sie ihn danach aus.

Hinweis zur Division von Integer-Werten:

  • / liefert das gegen 0 hin gerundete Resultat der Division.
    Beispiel: 16/6 gibt den Wert 2 zurück.
  • % liefert den Rest der Ganzzahl-Division.
    Beispiel: 16%6 gibt den Wert 4 zurück.

Aufgabe

Bitte versuchen Sie sich selbst am Figurenzeichnen mit Schleifen. Bearbeiten Sie die Turtle-Aufgaben vom Aufgabenblatt Greenfoot_Schleifen.pdf.