10: Programme strukturieren
- Strukturierte Zerlegung.
- Eigene Methoden programmieren.
- Parametervariablen nutzen.
Programme zur Lösung komplexerer Probleme können sehr lang werden. Um den Überblick zu behalten, macht es daher Sinn, das Programm in kleinere Teile zu zerlegen. Diese kannst du unabhängig voneinander programmieren und auf Fehler überprüfen, bevor du sie zur Gesamtlösung zusammensetzt.
Du kennst mittlerweile schon ein Beispiel dazu: Im aktiven Modus gibt es verschiedene Programmblöcke, die bestimmte Teile des Programms zusammenfassen. setup()
und draw()
sind Methoden, die festgelegte Programmteile darstellen. mousePressed()
ist ein anderes Beispiel.
Eigene Befehle programmieren
Das praktische ist, dass wir Methoden sehr einfach selber programmieren können:
void blauerKreis() {
fill(0, 150, 255);
stroke(0, 98, 195);
strokeWeight(2);
circle(mouseX, mouseY, 10);
}
Hier wird ein neuer Befehl stempeln()
definiert, der einen blauen Kreis an der Mausposition zeichnet. Sobald dieser neue Befehl definiert ist, kann er im Programm benutzt werden:
void setup() {
size(400, 400);
}
void draw() {
}
void mousePressed() {
blauerKreis();
}
void blauerKreis() {
fill(0, 150, 255);
stroke(0, 98, 195);
strokeWeight(2);
circle(mouseX, mouseY, 10);
}
Das ist praktisch, da wir nun alle Befehle, die zum Zeichnen eines blauen Kreises notwendig sind, in einem Block zusammengefasst haben und immer wieder benutzen können. Möchten wir nun ein rotes Quadrat "stempeln", statt eines blauen Kreises, können wir einfach eine neue Methode schreiben und diese benutzen:
void rotesQuadrat() {
fill(255, 126, 125);
stroke(148, 17, 0);
strokeWeight(1);
rectMode(CENTER);
rect(mouseX, mouseY, 10, 10);
}
- Schreibe das Programm oben so um, dass das rote Quadrat anstatt des blauen Kreises "gestempelt" wird.
- Schreibe das Programm dann so um, dass beide Figuren gleichzeitig gestempelt werden.
Befehle mit Eingaben programmieren
Eine Methode für sich ist nichts anderes als ein Algorithmus, wie wir sie schon die ganze Zeit programmieren. In Schritt 3 haben wir die ersten Algorithmen über ihre Eingabe und erwartete Ausgabe definiert. Zum Beispiel
Eingabe: drei Zahlen x, y und size
Ausgabe: Zeichne ein Quadrat mit Kantenlänge size um den Mittelpunkt (x, y)
Methoden können auch Eingaben empfangen und damit arbeiten. Du kennst das schon von Befehlen wie line(), der vier Zahlen als Eingabe bekommt und diese als Koordinaten für den Start- und Endpunkt einer Linie benutzt.
Den Algorithmus oben können wir so programmieren:
void quadrat( float x, float y, float size ) {
float r = size/2;
rect(x-r, y-r, size, size);
}
Beachte hier die verschiedenen Variablen:
float r
ist eine lokale Variable, die nur innerhalb des Blocks gültig ist.float x
,float y
undfloat size
sind Parametervariablen. Sie sind auch im Block gültig und repräsentieren die Eingabe der Methode.
Der Aufruf dieser neuen Methode erfolgt dann zum Beispiel so:
void draw() {
background(200);
quadrat(50, 100, 30);
quadrat(random(200), random(200), random(20));
}
Programmiere Methoden zu folgenden Definitionen:
- Ellipse
Eingabe: zwei Zahlen w und h Ausgabe: ein Oval in der Mitte des Fensters, mit der Breite <code>w</code> und Höhe <code>h</code>
- Ergänze die Ellipse oben um eine zufällige Farbe.
- Rechnen
Eingabe: zwei ganze Zahlen a und b Ausgabe: gib die Summe, Differenz, das Produkt und den Quotienten der Zahlen im Textfenster unten aus
- Ändere die Rechenfunktion oben so um, dass sie auch mit Kommazahlen funktioniert.
Wenn du Klausuren im Fach Informatik schreibst, dann bearbeite die Aufgaben oben am besten zunächst ohne Computer, um das Programmieren auf Papier zu üben.
void oval( float w, float h ) {
ellipse(width/2.0, height/2.0, w, h);
}
void setup() {
size(400, 400);
oval(160, 80);
}
void oval( float w, float h ) {
fill(random(255), random(255), random(255));
ellipse(width/2.0, height/2.0, w, h);
}
void setup() {
size(400, 400);
oval(200, 200);
oval(160, 80);
oval(80, 160);
}
void calc( int a, int b ) {
println("a = "+a+", b = "+b);
println("Summe: "+(a+b));
println("Differenz: "+(a-b));
println("Produkt: "+(a*b));
println("Quotient: "+(a/b));
}
void setup() {
calc(4, 7);
}
void calc( float a, float b ) {
println("a = "+a+", b = "+b);
println("Summe: "+(a+b));
println("Differenz: "+(a-b));
println("Produkt: "+(a*b));
println("Quotient: "+(a/b));
}
void setup() {
calc(4.0, 7.0);
}
- Programmiere eine Methode
greet
mit zwei Eingaben vom TypString
. Beim Aufrufgreet("Hallo", "Hans");
soll die AusgabeHallo, Hans!
auf die Zeichenfläche geschrieben werden. - Programmiere zwei Methoden
hallo(String name)
undservus(String name)
, die unter Verwendung vongreet
jeweils die AusgabenHallo, Hans!
undServus, Hans!
erzeugen.
Du kannst zwei Texte (Strings) mit einem +
verketten. Aus "Eins" + "Zwei"
wird EinsZwei
.
void greet( String greeting, String name ) {
fill(0);
text(greeting + ", " + name + "!", width/2, height/2);
}
void setup() {
size(400, 400);
greet("Hallo", "Hans");
}
void greet( String greeting, String name ) {
fill(0);
text(greeting + ", " + name + "!", width/2, height/2);
}
void hallo( String name ) {
greet("Hallo", name);
}
void servus( String name ) {
greet("Servus", name);
}
void setup() {
size(400, 400);
hallo("Hans");
translate(0, 16); // Verschiebt die Zeichenfläche etwas
servus("Hans");
}
- Programmiere zwei Methoden
void quadratWeiss(float x, float y)
undvoid quadratSchwarz(float x, float y)
, die jeweils ein weißes bzw. schwarzes Quadrat mit der Kantenlänge 40 an die Position(x, y)
zeichnen. - Benutze die beiden Methoden, um die Methoden
void zeileA(int nummer)
undvoid zeileB(int nummer)
zu programmieren, die eine Zeile der Zeichenfläche von links nach rechts abwechselnd mit weißen und schwarzen Quadraten füllt.zeileA
beginnt mit Weiß,zeileB
mit Schwarz. Dienummer
bestimmt die y-Koordinate der Zeile. Nummer 1 hat die Koordinate0
, Nummer 240
, usw. - Benutze
zeileA()
undzeileB()
, um die vollständige Zeichenfläche mit einem Schachbrettmuster zu füllen. - Bonus: Ändere die Methoden so ab, dass die Farben der Quadrate von weiß/schwarz auf beliebige andere Kombinationen geändert werden können.
Wiederhole Aufrufe von quadratWeiss()
und quadratSchwarz()
, bis die Breite der Zeichenfläche erreicht ist.
Die x-Koordinate erhöht sich pro Aufruf um die Kantenlänge (0
, 40
, 80
, ...). (Du kannst hier auch gut eine lokale Variable benutzen!)
Die y-Koordinate bleibt bei jedem Quadrat gleich und berechnet sich nach der Vorschrift (nummer-1)*40
.
void setup() {
size(200, 200);
}
void quadratWeiss(float x, float y) {
fill(255);
rect(x, y, 40, 40);
}
void quadratSchwarz(float x, float y) {
fill(0);
rect(x, y, 40, 40);
}
void setup() {
size(200, 200);
}
void zeileA( int nummer ) {
float y = (nummer-1)*40;
float x = 0;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
}
void zeileB( int nummer ) {
float y = (nummer-1)*40;
float x = 0;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
}
void quadratWeiss(float x, float y) {
fill(255);
rect(x, y, 40, 40);
}
void quadratSchwarz(float x, float y) {
fill(0);
rect(x, y, 40, 40);
}
void setup() {
size(200, 200);
}
void draw() {
background(200);
zeileA(1);
zeileB(2);
zeileA(3);
zeileB(4);
zeileA(5);
}
void zeileA( int nummer ) {
float y = (nummer-1)*40;
float x = 0;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
}
void zeileB( int nummer ) {
float y = (nummer-1)*40;
float x = 0;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
x += 40;
quadratWeiss(x, y);
x += 40;
quadratSchwarz(x, y);
}
void quadratWeiss(float x, float y) {
fill(255);
rect(x, y, 40, 40);
}
void quadratSchwarz(float x, float y) {
fill(0);
rect(x, y, 40, 40);
}
Befehle mit Rückgabe programmieren
Bisher haben wir die Ausgabe der Befehle direkt auf die Zeichenfläche oder in das Textfenster geschrieben. Oftmals ist es aber praktisch, die Ausgabe als Rückgabe zu erhalten, sodass wir damit weiterarbeiten können.
Wir könnten etwa eine Methode schreiben, die für den Flächeninhalt eines Kreises als Eingabe den zugehörigen Radius bestimmt:
float radius(float area) {
return sqrt(area/PI);
}
Wie du siehst, fehlt bei dieser Methode das allgegenwärtige void
. Stattdessen gibst du den Datentyp an, den das Ergebnis dieser Methode haben soll. Der eigentliche Wert des Ergebnisses (im Beispiel der Radius vom Datentyp float
) wird mit dem Befehl return
aus dem Block der Methode zurückgegeben. Das bedeutet anders gesagt: Wenn der Befehl radius()
benutzt wird, dann wird der Aufruf durch die passende Zahl ersetzt.
Nimm dieses Beispiel:
void draw() {
// Nichts
}
void mouseClicked() {
background(200);
circle(mouseX, mouseY, radius(1256.637));
}
float radius( float area ) {
return sqrt(area/PI);
}
Jeder Aufruf von radius(1256.637)
in Zeile 7 wird durch die Zahl 20.0
ersetzt (denn für einen Kreis mit dem Radius [math]\displaystyle{ 20 }[/math] gilt [math]\displaystyle{ A = 20 + \pi \approx 1256.637 }[/math]). circle() wird also mit circle(mouseX, mouseY, 20.0)
aufgerufen.
- Ändere das Programm oben so ab, dass bei einem Klick ein Kreis mit der Fläche
2000.0
gezeichnet wird. - Ändere das Programm noch einmal ab, sodass ein Kreis mit der Fläche
mouseX*mouseY
gezeichnet wird. - Gehe die Beispiele durch und schaue dir noch einmal die neuen Befehle an. Wofür steht
sqrt
?- Du kannst mathematische Funktionen (sin(), cos(), sqrt(), pow(), uvm.) jederzeit in deinem Programm einsetzen. Das ist besonders für Animationen hilfreich.
- Übernimm das folgende Beispiel in Processing, probiere es aus und ändere dann die Berechnung in
radius()
ab. Was passiert? void setup() { frameRate(60); } void draw() { float time = float(millis()) / 500.0; background(200); circle(width/2, height/2, radius(time)); } float radius( float i ) { return 90*sin(i); }
- Zusatz: Ergänze zwischen Zeile 8 und 9 die Zeile
farbe(time);
. Füge dann eine neue Methodevoid farbe( float i )
ans Ende ein. Die Methode soll mittels fill() die Farbe des Kreises abhängig von der Eingabei
ändern.
Eine mögliche Lösung:
void setup() {
frameRate(60);
}
void draw() {
float time = float(millis()) / 500.0;
background(200);
farbe(time);
circle(width/2, height/2, radius(time));
}
float radius( float i ) {
return 90*sin(i);
}
void farbe( float i ) {
fill(abs(255*cos(i)), abs(128*sin(i)), abs(255*sin(i)));
}