In der zweiten Übung beschäftigen wir uns
mit Arrays, in denen mehrere Elemente gespeichert
werden können, wie z.B. die Werte für jeden
Pixel eines Bildschirms.
Als Vorbereitung für spätere Projekte gibt
es dieses Mal außerdem neben public static
void main noch eine weitere Methode mit dem
Namen „aufgabe1“, die einen Test-Sysout
beinhaltet.
Der Code wartet in seiner Methode, bis er
in der Main aufgerufen wird und der Sysout
macht nun Platz für Arrays.
Sie sind ohne Importieren standardmäßig
in Java vorhanden und müssen lediglich deklariert
werden, wie das schon bei einzelnen Variablen
der Fall war.
Für ein Integer-Array beginnt es wie gewohnt
mit „int“, dem Namen und „=“, allerdings
kommen nun noch zwei eckige Klammern dazu.
Statt nur einem einzigen Wert, haben wir dieses
Mal vor, mehrere Werte zu speichern und deshalb
wird die Bezeichnung „new int[]“ gebraucht.
In den eckigen Klammern muss angegeben werden,
wie viele Slots das Array benötigt, um im
Speicher Planungssicherheit zu gewährleisten.
Das Zuweisen von Werten ist ebenfalls ähnlich
zum Zuweisen bei einfachen Variablen, allerdings
ist zusätzlich der genaue Index erforderlich,
an dem der Wert gespeichert werden soll.
Dies geschiet in eckigen Klammern, beginnend
mit dem Index 0.
Um den Wert 12 also an erster Stelle zu speichern,
wird als Position 0 angegeben.
Es folgt anschließend noch eine Zuweisung
für die zweite und fünfte Stelle im Array.
Um die Werte auf der Konsole auszugeben, gebe
ich im sysout noch einmal den Namen des Arrays
und in eckigen Klammern den Index an.
Wie erwartet wird dann der Wert unten angezeigt.
Für String-Arrays lassen sich wie bei Integern
auch zwischen die Deklaration zwei eckige
Klammern einfügen und der nachfolgende Teil
nutzt den new-Operator dieses Mal für ein
String-Array.
Alternativ lassen sich Arrays auch direkt
bei der Deklarierung befüllen, indem die
Werte in geschwungenen Klammern aufgelistet
werden.
Beim Ausführen des Codes werden die Arrays
soweit initalisiert, aber noch fehlen Anweisungen,
die richtig mit ihnen arbeiten.
Um sämtliche Werte eines Arrays auszugeben,
gibt es als Kurzform die foreach-Schleife,
deren Template mit dem Kürzel „foreach“
und Enter erzeugt werden kann.
Während des Durchlaufs wird im Integer „temp“
das aktuelle Element der genannten Liste gespeichert
und im Rumpf der Schleife fehlt nur noch der
sysout für temp.
Führe ich den Code aus, werden sämtliche
Elemente der Liste ausgegeben.
Lasse ich das „new line“ im sysout weg,
wird keine neue Zeile angefangen.
Möchte ich umgekehrt das gesamte Array mit
Werten füllen, stoße ich allerdings an die
Grenzen der For-Each-Schleife, da die Variable
temp nur den aktuellen Wert des Durchlaufs
speichert, der Indexwert, der für die Zuweisung
zwingend erforderlich ist, taucht dort allerdings
nicht auf.
Die Lösung bietet die klassische Fori-Schleife,
wie gewohnt mit einem Index i und als Limit
wird für das Array das Attribut length abgefragt.
Dank „i“ ist die Befüllung des Arrays
nun kein Problem mehr, für den Anfang entspricht
der Wert an der Stelle i dem Wert i.
Und nachdem im Fori-Teil das Array befüllt
wurde, kann foreach dieses Array fehlerfrei
ausgeben.
Statt jedoch schlicht und einfach den Wert
i für den Index i zu speichern, soll das
Array mit zufälligen Zahlen von 1-100 befüllt
werden.
Genutzt werden kann dafür eine weitere standardmäßig
in Java eingebaute Funktion mit dem Namen
random aus der Klasse „Math“, die ich
für den Anfang in einen Sysout einfüge.
Führe ich den Code aus, erhalte ich einen
double zwischen 0 und 1.
Für eine Zahl zwischen 0 und 100 wird der
Wert einfach mit 100 multipliziert.
Schlussendlich fehlt jetzt noch die Konvertierung
in einen Integer mit einem Int in Klammern
davor.
Als Ergebnis kommt jedoch jedes Mal eine 0
heraus, was reintheoretisch möglich ist,
jedoch in der Praxis daran liegt, dass der
zufällige Wert zwischen 0 und 1 bei der Integer-Konvertierung
auf 0 abgerundet und dann erst mit 100 multipliziert
wird.
Das Problem wird mit zwei weiteren Klammern
gelöst und das Ergebnis entspricht nun unseren
Wünschen.
Den Code können wir nun für die Arraybefüllung
einsetzen und erhalten als Ergebnis ein Array
bestehend aus Zufallszahlen.
Alternativ kann für Zufallszahlen statt Math.random
auch die Klasse Random genutzt werden, sie
muss allerdings vorher mit dieser Zeile importiert
werden.
Es wird dann ein neues Random-Objekt deklariert
und ihm ein Namen gegeben.
Nach dem „=“ folgt dann wie beim Array
der new-Operator für ein neues Random-Objekt.
Um eine Zufallszahl zu generieren wird für
die Instanz des Objektes die Funktion nextInt
aufgerufen und der maximale Wert angegeben.
Zurückgegeben wird schließlich die gewünschte
Zufallszahl als Integer und die Aufgabe2-Methode
ist somit wieder funktionsfähig.
Der nächste Schritt besteht in Aufgabe 3
nun darin, nicht nur eine Dimension zu speichern,
sondern zwei, etwa, wenn es um die Pixel eines
Bildschirmes geht.
Glücklichweise lässt sich dafür der Code
aus Aufgabe 2 receyceln, wir müssen bloß
die neue Dimension hinzufügen.
Im ersten Schritt wird mit einem weiteren
eckigen Klammerpaar die neue Dimension für
die Deklaration hinzugefügt.
Selbes Spiel gilt für die Zeile, in der das
Array befüllt wird.
Beim Ausführen des Programms würde nun die
Diagonale der Matrix mit Zufallszahlen befüllt
werden, weil sowohl der Wert für die Zeile,
als auch der Spalte pro Durchlauf um 1 erhöht
wird.
Was nun noch fehlt ist die Ausgabe auf der
Konsole, das geht mit foreach, wenn ich statt
einem einzelnen Integer ein gesamtes Integer-Array
in Empfang nehme, von temp in zeile umbenenne
und für dieses Integer-Array eine weitere
For-Each-Schleife programmiere.
Diese iteriert durch das Int-Array mit dem
Namen zeile von eben und speichert den Wert
in der Variable mit dem Namen „spalte“.
Wenn das erste Array durch ist, wird mit „nl“
eine neue Zeile angefangen und mit dem nächsten
Array fortgefahren, bis keines mehr übrig
ist. \t fügt außerdem zwischen die Werte
einen Tabulator.
Als Ergebnis erhalten wir dann die Ausgabe
einer sauber formatierten Matrix.
Die Ausgabe mit einer normalen For-Schleife
ist natürlich ebenfalls möglich, dort werden
genau so zwei For-Schleifen ineinander verschachtelt.
Allerdings wird bisher nur die Diagonale befüllt.
Die For-Schleife wird nur ein einziges Mal
durchlaufen und erzeugt eine zufällige Zahl
für die Indizes 0,0; 1,1 und 2,2.
Um stattdessen das gesamte Array zu befüllen,
wird eine zweite For-Schleife benötigt, welche
wie schon bei der Ausgabe von eben in die
erste verschachtelt wird.
Der Befehl „matrix [i],[j] = random“ wird
ausgeführt, während der Spaltenindex „j“
von 0 bis 2 iteriert.
Ist die Zeile vollständig wird der Zeilenindex
i um 1 erhöht und die Spalten-Schleife mit
j beginnt von vorne.
Und nun können wir mit Shift+F10 den Code
ausführen und das Ergebnis kontrollieren.
Zuerst wird das Array mit Zufallszahlen befüllt
und im zweiten Schritt auf der Konsole ausgegeben.
In Methoden ausgedrückt wäre das zuerst
„arraybefuellen()“ und dieses Array wird
an die Methode „arrayausgeben()“ weitergeleitet.
Um dieses Konzept weiter zu vertiefen, wird
in den nächsten Aufgaben eine virtuelle Roboter-Schildkröte
programmiert.
Im heruntergeladenen Template-Projekt befindet
sich dafür eine Zeile, in der ein Turtle-Objekt
erstellt wird, das den Namen „t“ trägt.
Der Code dafür ist in die Klasse Turtle ausgelagert.
Mit t.laufe(100) läuft diese Turtle beispielsweise
100 Einheiten vorwärts und t.zeichne() gibt
ganz am Ende dann den gelaufenen Weg auf der
Leinwand aus.
Um nachzusehen, welche Funktionen der Turtle
zur Verfügung stehen, reicht es aus, „t.“
einzutippen, um von IntelliJ die genaue Liste
vorgeschlagen zu bekommen.
Für den Anfang reichen uns laufe() mit der
Distanz und drehe() mit dem Winkel.
Um ein Quadrat mit der Seitenlänge 100 zu
zeichnen, erstelle ich eine public static
void viereck() und dort läuft die Turtle
für die erste Seite 100 Schritte, dreht sich
danach um 90 Grad und das ganze macht sie
insgesamt vier Mal.
Rufe ich die Viereck-Methode in der Main auf,
wird, wenn alles nach Plan läuft, nun die
erste Form gezeichnet.
Um anschließend ein Fünfeck zu zeichnen,
kann ich im Prinzip die Methode für das Viereck
kopieren, in fünfeck() umbenennen und die
Werte anpassen.
Statt vier Seiten zu zeichnen, werden es für
das Fünfeck 5 und der Winkel ist 360/5, das
macht 72°.
Außerdem packe ich laufe(100) und drehe(72)
in eine For-Schleife, die fünf Mal durchlaufen
wird, dann muss ich den Wert nur an einer
Stelle ändern.
Und rufe ich fuenfeck() auf, zeichnet die
Turtle ein Fünfeck.
Um in Aufgabe 6 jedes beliebige N-Eck zu zeichnen,
kann wieder der Fünfeck-Code receycelt werden
mit dem Zusatz, dass eine Variable für die
Anzahl der Ecken in den Klammern definiert
wird.
Die Methode empfängt also den Wert der Ecken
und deshalb wird die Variable dann für die
Fünf des Fünfecks eingesetzt. 72 war das
Ergebnis von 360/5 und daraus wird 360/anzEcken.
Führe ich diesen Code dann direkt aus, z.B.
für ein Achteck, sieht es so aus, als ob
alles funktioniert und die Arbeit getan ist,
allerdings gibt es beim Siebeneck noch ein
kleines Problem.
Die letzte Seite schließt nämlich nicht
nahtlos an die erste Seite an, weil es einen
Rundungsfehler bei der Berechnung des Winkels
gibt. 360/7 ergibt 51,4 Grad, bislang wird
dieser Wert aber auf 51 Grad abgerundet und
so entsteht die Ungenauigkeit.
Gelöst wird das Problem, indem der Datentyp
vom Integer in einen double geändert wird,
konkret wird der 360 also eine Kommastelle
hinzugefügt und beim Ausführen gibt es nun
keine Unstimmigkeiten mehr.
Als nächstes ist dann eine Spirale an der
Reihe.
Das schwierigste daran ist eigentlich nur
die erste Umdrehung, denn danach läuft sie
einfach immer so weiter.
Primitiv könnte man mit einem Viereck anfangen,
das unendlich oft gezeichnet wird.
Die Viereckmethode bestand aus laufen(100)
und drehe(90) und wenn ich statt einer For-
eine While-Schleife benutze, geschiet dies
auch tatsächlich unendlich oft – so undendlich
oft, dass die Ausführung von schnecke() nie
aufhört und t.zeichne damit vergeblich wartet.
Über Run und „Stop Haupt“ kann das Programm
aber zum Glück gestoppt werden.
Um aus dem unendlichen Viereck eine Spirale
zu machen, führe ich die Variable laenge
ein, lasse die While-Schleife nur noch so
lange ausführen, bis die laenge von 100 erreicht
ist, die Turtle läuft dann die anfangs definierte
Länge und schließlich erhöhe ich den Wert
für die Länge, damit die Schnecke größer
wird.
Und damit ist der erste Prototyp einer Spirale
erreicht.
Verringere ich für eine runde Spirale den
Winkel, wird aber noch ein Problem deutlich.
Die Spirale gewinnt schnell an Länge, aber
der Winkel kommt nicht schnell genug mit.
Schärfere Kurven mit 20° machen die Spirale
wieder zackiger.
Also muss der Betrag nur sporadisch erhöht
werden.
Dafür verschiebe ich den Drehen-Laufen-Code
in eine For-Schleife mit z.B.
40 Durchläufen, drehe um 5 Grad und erhöhe
erst danach den Laufweg.
Das Ergebnis entspricht jetzt den Vorstellungen.
Mit ein paar weiteren Änderungen an den Parametern
lassen sich zudem richtige kleine Kunstwerke
erzeugen.
(100 Grad Vierecke oder inneinander größer
werdene Formen vom selben Ursprung)
Beim Pentagram in der nächsten Aufgabe ist
die Vorgehensweise ähnlich.
Dieses Mal läuft die Turtle eine Strecke,
dreht sich dann um 180 Grad und läuft die
selbe Strecke zurück.
Insgesamt werden 5 Striche gezeichnet, also
kann ich die Pendler-Turtle schonmal in die
entsprechende For-Schleife stecken.
Als Winkel für einen Zacken war 36° gegeben
und damit nicht mehr der selbe Weg zurückgelegt
wird, reduziere ich die 180°-Drehung um genau
diesen Wert.
Mit der Variable „laenge“ lässt sich
außerdem beim Methodenaufruf individuell
die Länge einstellen.
Das war es dann auch schon, denn nun wird
ein sauberes Pentagram gezeichnet und damit
haben wir für die nächste Aufgabe, in der
es darum geht, einen Ring aus Sternen zu zeichnen,
bereits jene Funktion, welche die Sterne zeichnet,
sie müssen lediglich noch auf einem Kreis
platziert werden.
Zum Einsatz kommen dabei die Funktionen t.setxPos
und t.setyPos, welche die X- bzw.
Y-Koordinaten der Turtle bestimmen.
Einen Kreis mit dem Radius 100 aus vier Sternen
lässt sich in kartesischen Koordinaten damit
ziemlich leicht zeichnen.
Es wird davon ausgegangen, dass der Mittelpunkt
des Kreises sich bei 200,200 befindet.
Im ersten Schritt werden die Koordinaten auf
300,200 gesetzt, dann die Pentagram-Methode
aufgerufen, um die Turtle zu zeichnen und
schließlich zur nächsten Koordinate gegangen,
das wäre dann 200,300.
Und nochmal das ganze mit 100,200 und 200,100.
Für 0, 90, 180 und 270° lassen sich die
kartesischen Koordinaten problemlos im Kopf
berechnen, darüber hinausgehend macht es
jedoch Sinn, auf Polarkoordinaten auszuweichen,
bei denen die Position in Sinus und Kosinus
ausgedrückt wird.
Für den ersten Stern bei 0 Grad hat die Turtle
beim Kosinus den maximalen Wert 1 und foglich
ist der Sinus 0.
90 Grad weiter ist der Sinus bei 1 und Cos
bei 0.
Nach der Strecke pi ist der Kosinus negativ
im Extrem, der sinus wieder bei 0 und schließlich
bei 270° der Kosinus 0 und der Sinus am Tiefpunkt.
Umgesetzt in Java definiere ich in der Sternenring-Methode
eine Variable für den Winkel und setze den
Wert für den Anfang auf 0.
Danach setze ich den Wert der X-Koordinate
auf 200 + den Radius multipliziert mit dem
Kosinus des Winkels.
Dafür nutze ich Math.cos und würde an ihr
den Wert von Winkel weitergeben, wenn ich
nicht vorher noch den Wert in Radiant konvertieren
müsste, weil die Kosinus-Funktion dies so
verlangt.
360 Grad müssen beispielsweise in 2π konvertiert
werden, bzw. 180 in π.
Also teile ich den Wert durch 180 und multipliziere
ihn mit π.
Math.toRadians lässt sich an dieser Stelle
selbstverständlich auch verwenden.
Das gleiche mache ich dann noch für den Sinus
- zur Erinnerung: Die 200 sind der Wert vom
Mittelpunkt und dazu werden 100 vom Radius
addiert die wiederum mit dem Sinus zwischen
1 und -1 multipliziert werden.
Als nächstes wird dann der Wert vom Winkel
um 90 erhöht und damit ich mich nicht wiederhole
packe ich die Schritte in eine Forschleife,
die für 4 Sterne 4 Mal durchlaufen wird.
Schließlich kann ich das Programm ausführen
und kontrollieren, ob die Sterne korrekt gezeichnet
wurden.
Und dank der Polarkoordinaten lässt sich
nun die Anzahl der Sterne beliebig einstellen,
für 6 Sterne wird 360 durch 6 geteilt und
demzufolge der zweite Stern bei 60 Grad gezeichnet,
es wird also für die X-Koordinate der Radius
mit cos(60) also 0,5 multipliziert und der
Wert für Y beträgt sin(60)*100 also ungefähr
86,6.
Für die automatische Berechnung in Java führe
ich eine Variable für die Anzahl der Sterne
ein, die For-Schleife wird genau so oft durchlaufen
und der Winkel um 360/Anzahl erhöht.
Für die EU-Flagge wären das beim Aufruf
der Methode 12 Sterne.
Vielen Dank für Ihre Aufmerksamkeit.
