10: Programme strukturieren

Aus Informatik-Box
Zur Navigation springen Zur Suche springen
Icon Chalk board.png
Was du in diesem Schritt lernst
  • 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);
}
Processing 2021 logo.svg
Arbeitsauftrag
  1. Schreibe das Programm oben so um, dass das rote Quadrat anstatt des blauen Kreises "gestempelt" wird.
  2. Schreibe das Programm dann so um, dass beide Figuren gleichzeitig gestempelt werden.
    Klicken, um das Programm zu starten.


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 und float 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));
}
Processing 2021 logo.svg
Arbeitsauftrag

Programmiere Methoden zu folgenden Definitionen:

  1. 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>
    
  2. Ergänze die Ellipse oben um eine zufällige Farbe.
  3. Rechnen
    Eingabe: zwei ganze Zahlen a und b
    Ausgabe: gib die Summe, Differenz, das Produkt und den Quotienten der Zahlen im Textfenster unten aus
    
  4. Ändere die Rechenfunktion oben so um, dass sie auch mit Kommazahlen funktioniert.
Icon Warning.png

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.

Aufgabe 1
void oval( float w, float h ) {
  ellipse(width/2.0, height/2.0, w, h);
}

void setup() {
  size(400, 400);
  oval(160, 80);
}
Aufgabe 2
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);
}
Aufgabe 3
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);
}
Aufgabe 4
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);
}


Processing 2021 logo.svg
Arbeitsauftrag
  1. Programmiere eine Methode greet mit zwei Eingaben vom Typ String. Beim Aufruf greet("Hallo", "Hans"); soll die Ausgabe Hallo, Hans! auf die Zeichenfläche geschrieben werden.
  2. Programmiere zwei Methoden hallo(String name) und servus(String name), die unter Verwendung von greet jeweils die Ausgaben Hallo, Hans! und Servus, Hans! erzeugen.
Icon Info.png

Du kannst zwei Texte (Strings) mit einem + verketten. Aus "Eins" + "Zwei" wird EinsZwei.

Aufgabe 1
void greet( String greeting, String name ) {
  fill(0);
  text(greeting + ", " + name + "!", width/2, height/2);  
}


void setup() {
  size(400, 400);
  greet("Hallo", "Hans");
}
Aufgabe 2
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");
}


Processing 2021 logo.svg
Arbeitsauftrag
  1. Programmiere zwei Methoden void quadratWeiss(float x, float y) und void quadratSchwarz(float x, float y), die jeweils ein weißes bzw. schwarzes Quadrat mit der Kantenlänge 40 an die Position (x, y) zeichnen.
  2. Benutze die beiden Methoden, um die Methoden void zeileA(int nummer) und void 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. Die nummer bestimmt die y-Koordinate der Zeile. Nummer 1 hat die Koordinate 0, Nummer 2 40, usw.
  3. Benutze zeileA() und zeileB(), um die vollständige Zeichenfläche mit einem Schachbrettmuster zu füllen.
  4. Bonus: Ändere die Methoden so ab, dass die Farben der Quadrate von weiß/schwarz auf beliebige andere Kombinationen geändert werden können.
Tipp zu Aufgabe 2

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.

Aufgabe 1
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);
}
Aufgabe 2
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);
}
Aufgabe 3
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);
}
Icon Info.png
Etwas Mathematik: Die Formel für den Flächeninhalt eines Kreises [math]\displaystyle{ A = \pi r^2 }[/math] lässt sich durch Umstellen nach [math]\displaystyle{ d }[/math] auflösen: [math]\displaystyle{ \sqrt{\frac{A}{\pi}} = d }[/math]. Voilà!

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.

Processing 2021 logo.svg
Arbeitsauftrag
  1. Ändere das Programm oben so ab, dass bei einem Klick ein Kreis mit der Fläche 2000.0 gezeichnet wird.
  2. Ändere das Programm noch einmal ab, sodass ein Kreis mit der Fläche mouseX*mouseY gezeichnet wird.
  3. 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 Methode void farbe( float i ) ans Ende ein. Die Methode soll mittels fill() die Farbe des Kreises abhängig von der Eingabe i ändern.
    Klicken, um das Programm zu starten.
Mögliche Lösung zu 3

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)));
}


Processing 2021 logo.svg
Sprinteraufgabe

Erstell dir eine Kopie des Schachbretts von oben und öffne sie. Animiere das Schachbrett mit den mathematischen Funktionen, die du kennengelernt hast. Sei kreativ!

Klicken, um das Programm zu starten.