Greenfoot: Schleifen Kommen wir noch einmal zurück zum etwas langweiligen, aber schön einfachen ausgabe_in_konsoleSzenario (ö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 Instanzvariable – also eine Variable, 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 Zahl einmal auf die ActSchaltfläche zu drücken. Jetzt hätten wir gerne, dass ein einziger Aufruf der act()-Methode eine Zahl gleich mehrfach ausgibt, z.B.: 7777777777 Die Frage ist also, wie wir dem Computer sagen, er soll mehrmals dasselbe tun, 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. 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 das Abbruchkriterium, ä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 Code immer wieder ausgeführt. • An der dritten Stelle steht eine Anweisung, die bei 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 Ausgaben 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): 1111111111 2222222222 3333333333 .... In der folgenden Variante wird die Zählvariable gleich noch innerhalb des Code-Blocks benutzt, das ist oft sehr praktisch. Versuchen Sie zuerst herauszufinden, wie die Ausgabe aussehen wird, danach ausprobieren: for (int i = 1; i<=10; i++){ System.out.print(i + “ “); } Und wie sieht es mit dieser Variante aus? for (int i = 10; i>0; i=i-2){ System.out.print(i + “ “); } 2. Die while-­‐Schleife 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. Die erste Variante oben 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 (s. Wiki), vielleicht hilft das bei der Vermeidung. 3. Aufgaben zu Schleifen (Konsolenausgabe) 1) Schreiben sie ein Programm, das mithilfe einer for-Schleife die Zahlen 2 bis 20 ausgibt. 2) Ändern sie den Code aus Aufgabe 1) so ab, dass dieselbe Ausgabe mithilfe eine while-Schleife entsteht. 3) Benutzen Sie eine beliebige Schleife, um die Siebenerreihe (zwischen 0 und 100) auszugeben. 4) Benutzen Sie eine beliebige Schleife, um die Dreiherreihe rückwärts (zwischen 30 und 0) auszugeben. 5) Schreiben Sie ein Programm, das die Summe der ganzen Zahlen von 1 bis 100 mit einer Schleife berechnet und ausgibt. 6) Schreiben Sie ein Programm, das die Summe der Quadratzahlen von 1 bis 10 mit einer Schleife berechnet und ausgibt. 7) Ein Programm soll zuerst von 0 bis 0, dann von 0 bis 1, dann von 0 bis 2, dann 0 bis 3, usw., bis 0 bis 9 zählen. Die Ausgabe soll so aussehen (eine Zeile pro act()-Aufruf): 0 01 ... 0123456789 Hinweis: Sie haben oben schon gesehen, wie man in einer Zeile bis zu einer bestimmten Zahl hochzählt. Das müssen Sie jetzt nur noch kombinieren mit einer Instanzvariablen für die aktuelle Obergrenze, die selbst bei jedem act()-Aufruf um 1 inkrementiert (erhöht) wird. 8) Entwickeln Sie Programme, die die angegebenen Sternchenmuster auf möglichst einfache Weise unter Benutzung von Zählschleifen ausgeben (eine Zeile pro act()-Aufruf): 1. **** **** **** **** 2. * ** *** **** ***** 3. **** *** ** * 4. ** **** ****** ******** 5. * ** *** ** * 9) Schreiben Sie ein Programm das eine Schleife benutzt, um folgende Ausgabe zu erzeugen: 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 10) Das kleine 1x1 soll in zehn Zeilen ausgegeben wrden, ungefähr so: 1 2 3 4 5 6 7 8 9 10 2 4 6 8 10 12 14 16 18 20 3 6 9 12 15 .... Hinweis: Das geht sehr ähnlich wie in den obigen Aufgaben, aber schaffen Sie es auch, dass alle zehn Zeilen auf einmal ausgegeben werden, wenn man einmal auf Act() drückt? 11) Schaffen Sie es mithilfe der folgenden Informationen und einer Schleife, ihren Namen rückwärts auszugeben? String meinName = „Nicolas Ruh“; String erstesZeichen = meinName.substring(0,1); //gibt das erste Zeichen //(Position 0 bis 1) von meinName zurück System.out.print(meinName.substring(1,2)); //gibt das zweite Zeichen aus 4. 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 – wie üblich müssen Sie es herunterladen, entpacken, und in Greenfoot öffnen. Abb 1: So sollte das Szenario zu Beginn aussehen, es passiert noch nichts, wenn man auf den Act-Knopf drückt. 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 Wombat, 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 habe ich ihnen hier zusammengestellt: Gegeben ist eine Klasse Turtle, die u.a. folgende Methoden zur Verfügung stellt: void moveAndDraw(int distance) Bewegt die Turtle geradeaus und zeichnet gleichzeitig eine Line in der aktuellen Stiftfarbe void moveToAndDraw(int newX, int newY) Bewegt die Turtle zu den angegebenen Koordinaten und zeichnet gleichzeitig eine Line in der aktuellen Stiftfarbe void setColor(String newColor) Ändert die Stiftfarbe. Möglich: "red", "black", "blue", "yellow", "green", "magenta", "white", "gray" In der Actor-Klasse sind seit der Greenfoot-Version 2.1 u.a. die folgenden praktischen Methoden definiert: void move(int distance) Move this actor the specified distance in the direction it is currently facing. void setLocation(int x, int y) Assign a new location for this actor. void turn(int amount) Turn this actor by the specified amount (in degrees) 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 ich also lediglich dafür sorgen, dass in den Klammern ein ganzzahliger Wert steht, dann erfolgt eine Bewegung über die entsprechende Distanz. Wenn ich also will, dass Joe sich um 10 Pixel geradeaus bewegt, dann brauche ich den Befehl move(10); wenn er gleichzeitig noch einen Strich zeichnen soll, benutze ich moveAndDraw(10); mit turn(90); bringe ich Joe dazu, sich um 90 Grad (im Uhrzeigersinn) zu drehen. Anstatt den Wert direkt in die Klammern zu schreiben kann ich 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 Wombat-Szenatio 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); } } Vielleicht überlegen Sie zunächst, was dieser Code wohl bewirken könnte und probieren es dann aus. So, und nun versuchen Sie sich selbst am Figurenzeichnen mit Schleifen: 5. Turtle Aufgaben 1. Ausgehend vom letzten Beispiel: ändern Sie den Code so ab, dass Joe kein Quadrat, sondern ein Dreieck zeichnet. 2. Ändern Sie Joes act()-Methode so, dass Joe beim Aufruf der Methode GENAU das rechtsstehende Bild produziert (die innerste, kürzeste Linie ist 10 Pixel lang, jede weitere ist um 10 Pixel länger als die Vorhergehende). 3. Ändern Sie Joes act()-Methode so, dass Joe beim Aufruf der Methode GENAU das rechtsstehende Bild produziert (die Seitenlänge eines Quadrats ist 30 Pixel, der Abstand zwischen Quadraten ebenfalls). 4. Ändern Sie Joes act()-Methode so, dass Joe beim Aufruf der Methode GENAU das rechtsstehende Bild produziert (Strichlänge ist je 100 Pixel). 5. Ändern Sie Joes act()-Methode so, dass Joe beim Aufruf der Methode GENAU das rechtsstehende Bild produziert (die Strichlängen sind 10 resp. 100 Pixel).