Behandelte Konzepte/Konstrukte: Funktionsdefinition, Parameter, Rückgabewert, Überladen, Signatur, Skopus

Lernziele

  • Sie verstehen die Bestandteile und den Aufbau einer Funktion, inklusive Parameter und Rückgabetyp/-wert
  • Sie können eigene Funktionen definieren
  • Sie können Funktionen in verschiedenen Kontexten aufrufen
  • Sie können Arrays als Parameter und als Rückgabewert von Funktionen einsetzen
  • Sie verstehen, in welchen Situationen eine neue Funktion sinnvoll ist, um den Code übersichtlicher zu gestalten oder um Codeteile wiederverwendbar zu machen
  • Sie können mehrere Funktionen mit gleichem Namen definieren (Überladen) und per Signatur unterscheiden
  • Sie verstehen die Reichweite (Skopus) von Variablen und können den Skopus von konkreten Variblen erklären

Voraussetzungen

Sie sollten sicher mit Variablen umgehen können Kapitel 2.

Sie sollten sicher mit If-Anweisungen umgehen können Kapitel 3.

Ab Abschnitt 7.4 sollten Sie auch sicher mit Schleifen Kapitel 5 sowie mit Arrays Kapitel 6 und Objekten Kapitel 4 umgehen können.

Neueste Aktualisierungen (zuletzt 23.01.2024)
  • 23.01.2024: Level zu Aufgaben hinzugefügt
  • 02.10.2021: Lernziele angepasst
  • 02.08.2021: Neue Kapitelnummerierung

Funktionen sind neben Variablen und Kontrollfluss-Strukturen (if, while, for) der dritte Pfeiler Ihrer Programmierausbildung. Mit Funktionen wird Ihr Code strukturierter und lesbarer. Noch wichtiger: Teile Ihres Codes werden wiederverwendbar.

7.1 Was ist eine Funktion?

Stellen Sie sich vor, Sie könnten ein paar Zeilen Code in ein Paket packen und ein Schild mit foo aufs Paket kleben. Jedesmal, wenn Sie diese Zeilen in Ihrem Code ausführen wollen, brauchen Sie nur foo hinschreiben. foo ist eine Funktion!

Sie haben übrigens schon Funktionen benutzt, in der Form von "Befehlen" wie size(), ellipse() und rect() sind Funktionen, die das Processing-Team für Sie geschrieben hat. In diesem Kapitel lernen Sie, wie Sie selbst solche Befehle bereitstellen.

Video: Funktionen 1 (5:02)

Funktion definieren

Bevor Sie eine neue Funktion verwenden können, müssen Sie Processing sagen, was diese Funktion genau tun soll. Das machen Sie mit der Definition der Funktion.

Allgemein definieren Sie eine (einfache) Funktion wie folgt:

void NAME() {
   CODE
}

Wobei CODE für eine beliebige Anzahl von Codezeilen stehen kann. Diese Funktion hier zeichnet zum Beispiel ein Gesicht mit zwei schwarzen Knopfaugen an der Stelle (x, y), wobei x und y bereits oben im Code deklariert sein müssen:

void zeichneGesicht() {
   fill(255); // weiß
   ellipse(x, y, 80, 80); // Gesicht
   fill(0); // schwarz
   ellipse(x-15, y-15, 20, 20); // Auge
   ellipse(x+15, y-15, 20, 20); // Auge
}

Achten Sie darauf, Funktionen immer klein zu schreiben. Funktionen können nur im aktiven Modus definiert werden, d.h. Sie müssen entweder setup() oder draw() in Ihrem Code haben.

Etwas merkwürdig sehen noch die runden Klammern hinter dem Funktionsnamen aus, diese bekommen im Abschnitt Parameter Sinn. Als nächstes erfahren Sie, wie Sie Ihre neue Funktion denn verwenden.

Funktion aufrufen

Wir haben oben die Funktion zeichneGesicht definiert. Diese steht uns ab sofort als Befehl zur Verfügung, genauso wie die "eingebauten" Befehle ellipse, size, stroke usw.

Sie rufen den Befehl dann so auf:

zeichneGesicht();

Sehen wir uns das vollständige Programm an:

int x = 50;
int y = 50;

void setup() { // nichts zu tun
}

void draw() {
   // sehr übersichtlich :)
   background(255);
   zeichneGesicht(); // Funktionsaufruf
   x++;
}

// Funktionsdefinition:
void zeichneGesicht() {
   fill(255);
   ellipse(x, y, 80, 80);
   fill(0);
   ellipse(x-15, y-15, 20, 20);
   ellipse(x+15, y-15, 20, 20);
}

Der Vorteil hier ist zunächst mal, dass Ihr Programm übersichtlicher wird. Wenn man in draw() hineinschaut, sieht man direkt, dass offenbar etwas entlang der x-Achse bewegt wird. Der Name der Funktion zeichneGesicht sagt klar, was bei Aufruf des Befehls zu erwarten ist. Das heißt: Funktionen helfen, Code übersichtlicher und lesbarer zu gestalten. Man spricht auch von selbsterklärendem Code, d.h. der Code spricht für sich selbst und muss nicht (oder kaum) durch Kommentare ergänzt werden. Dabei ist wichtig, aussagekräftige Namen für die Funktionen zu wählen.

Fingerübungen

a) Hello, world!

Schreiben Sie die Funktion hello, die einfach "hello, world" auf die Konsole schreibt.

Da man für die Nutzung von Funktionen im aktiven Modus sein muss, benötigen Sie ein setup oder draw. Wenn wir keine Animation/Interaktion brauchen, reicht ein setup.

Testen Sie Ihren Code mit:

void setup() {
  hello();
  hello();
  hello();
}

Sie sollten sehen:

hello, world
hello, world
hello, world
Lösung
void hello() {
  println("hello, world");
}

Übungsaufgaben

7.1 a) Mauszeiger   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion mauszeiger, welche an Position der Maus einen Kreis mit Durchmesser 20 zeichnet.

Testen Sie Ihren Code mit

void draw() {
  background(255);
  mauszeiger();
}

7.1 b) Code durch Funktion ersetzen   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie sollen folgenden Code "aufräumen". Im Code werden ein Haus und eine Sonne gezeichnet. Definieren Sie eine neue Funktion zeichneHaus, in dem das Haus gezeichnet wird und rufen Sie diese Funktion in draw auf. Tun Sie das gleiche für die Sonne mit einer Funktion zeichneSonne.

void setup() {
  size(300, 300);
}

void draw() {
  background(#31F0FF);
  noStroke();
  fill(#FF3134);
  rect(80, 200, 80, 80);
  triangle(80, 200, 160, 200, 120, 150);
  fill(#FFF931);
  ellipse(200, 100, 80, 80);
}

7.1 c) Dreierlei   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie drei Funktionen one, two, three. Die Funktionen geben jeweils die Zahl 1, 2 und 3 auf der Konsole aus.

Rufen Sie in setup die Funktion one auf.

In der Funktion one rufen Sie two auf.

In der Funktion two rufen Sie three auf.

void setup() {
  one();
}

Sie sollten sehen:

1
2
3

Wenn Sie Ihren Code wie folgt testen wollen, haben Sie etwas falsch gemacht. Lesen Sie nochmal die Aufgabenbeschreibung.

void setup() {
  one();
  two();
  three();
}

Zusammenfassung

Sie können eigene Funktionen definieren, die Sie dann im Code beliebig oft aufrufen können. Allgemein sieht eine Funktionsdefinition so aus:

void NAME() {
  CODE
}

Funktionen dienen dazu, den Code übersichtlicher und lesbarer zu gestalten. Dazu sollten die gewählten Funktionsnamen möglichst aussagekräftig sein.

Eigene Funktionen kann man nur im aktiven Modus definieren.

Funktionsnamen schreibt man immer klein.

7.2 Parameter

Sie haben oben nur die einfachste Variante einer Funktion gesehen. Wenn Sie sich den Befehl rect ansehen, dann bekommt der Befehl noch einige Infos mit auf den Weg, nämlich Eckpunkt, Breite und Höhe:

rect(50, 50, 100, 80);

Diese Zusatzinformationen nennt man Parameter. Parameter geben Ihnen die Möglichkeit, viele Varianten einer Aufgabe ("zeichne ein Rechteck!") mit der selben Funktion (rect) zu erfassen, indem die jeweils spezifische Ausprägung (Position x:50 y:50, Breite 100, Höhe 80) in den Parametern gesetzt wird.

Video: Funktionen 2 - Parameter (6:11)

Parameter in der Funktionsdefinition

Damit Ihre Funktion Parameter entgegennehmen kann, müssen Sie diese bei der Funktionsdefinition mitdefinieren. Zum Beispiel wollen Sie der Gesichtsfunktion die Augenfarbe mitgeben:

void zeichneGesicht(int augenfarbe) {
   fill(255); // weiß
   ellipse(x, y, 80, 80); // Gesicht
   fill(augenfarbe);
   ellipse(x-15, y-15, 20, 20); // Auge
   ellipse(x+15, y-15, 20, 20); // Auge
}

Sie sehen, dass Sie in den Klammern zwei Dinge hinzufügen: den Typ des Parameters und den Namen. Im Grunde ist das exakt das gleiche wie bei einer Variable, die Sie ganz oben im Code deklarieren.

Und tatsächlich: der Parameter augenfarbe ist eine Variable.

Parameter ist lokale Variable

Ein Parameter ist eine lokale Variable innerhalb der Funktion. Das heißt, dass diese Variable eine begrenzte Lebensdauer hat. Sie beginnt bei der öffnenden geschweiften Klammer und endet bei der schließenden. Außerhalb des Funktionskörpers kann auf Parameter nicht zugegriffen werden.

Sie können - durch Komma getrennt - mehrere Parameter definieren, z.B. die Farbe von sowohl Gesicht als auch Augen von außen bestimmen lassen:

void zeichneGesicht2(int gesichtsfarbe, int augenfarbe) {
   fill(gesichtsfarbe);
   ellipse(x, y, 80, 80); // Gesicht
   fill(augenfarbe);
   ellipse(x-15, y-15, 20, 20); // Auge
   ellipse(x+15, y-15, 20, 20); // Auge
}

Die allgemeine Form der Funktionsdefinition mit Parametern ist:

void NAME(TYP1 PARAM1, TYP2 PARAM2, ...) {
   CODE
}

Im folgenden Beispiel sind die Parameter a und b nur im Code der Funktion printMittelwert gültig.

void setup() {
  printMittelwert(5.0, 12.5);
}

void printMittelwert(float a, float b) {
  float summe = a + b;
  println(summe / 2);
}

Jeder Versuch, die Parameter a und b z.B. in setup() zu verwenden, wird mit einer Fehlermeldung bestraft, denn die Variablen existieren nur innerhalb der Funktionsdefinition.

void setup() {
  printMittelwert(5.0, 12.5);
  println(a); // Fehler! Variable a ist hier nicht gültig.
}

void printMittelwert(float a, float b) {
  float summe = a + b;
  println(summe / 2);
}

Parameter nicht verändern

Innerhalb einer Funktion gilt für die Parameter:

Verändern Sie innerhalb einer Funktion niemals einen der Parameter.

Hier ein Beipiel, was gemeint ist:

// Schlechter Stil!

void printSumme(int a, int b) {
  a = a + b; // Parameter a wird verändert
  println(a);
}

Der obere Code führt zwar nicht zu einem Fehler, zeigt aber schlechten Stil, da bei längerem Code unklar ist, wann der Parameter a den ursprünglichen Wert hat und wann einen veränderten Wert.

Im Beispiel können Sie stattdessen eine neue lokale Variable einführen:

// Stattdessen lokale Variable:

void printSumme(int a, int b) {
  int c = a + b;
  println(c);
}

In diesem Beispiel können Sie natürlich auch viel einfacher dies schreiben:

void printSumme(int a, int b) {
  println(a + b);
}

Diese Regel gilt nur für primitive Datentypen (int, float, boolean ...). Bei Objekten (z.B. PVector) und Arrays (sind ebenfalls Objekte) kann es sein, dass man will, dass ein Parameter und damit das referenzierte Objekt verändert wird.

Aufruf mit Parametern

Beim Aufruf einer Funktion mit Parametern, müssen Sie darauf achten, dass Sie genau die Information mitgeben, die Ihre Definition verlangt. Das heißt, die Anzahl, Reihenfolge und Datentypen der Parameter müssen mit den Angaben Ihrer Funktionsdefinition übereinstimmten. Dies hängt mit der Signatur der Funktion zusammen (siehe entsprechenden Abschnitt unten).

Schauen wir uns nochmal die zwei Funktionen von oben an. Zunächst nochmal die Definitionen:

void zeichneGesicht(int augenfarbe) {
  fill(255);
  ellipse(x, y, 80, 80);
  fill(augenfarbe);
  ellipse(x-15, y-15, 20, 20);
  ellipse(x+15, y-15, 20, 20);
}

void zeichneGesicht2(int gesichtsfarbe, int augenfarbe) {
  fill(gesichtsfarbe);
  ellipse(x, y, 80, 80);
  fill(augenfarbe);
  ellipse(x-15, y-15, 20, 20);
  ellipse(x+15, y-15, 20, 20);
}

Diese Aufrufe sind falsch:

zeichneGesicht(36.5); // falscher Typ, muss int sein
zeichneGesicht(50, 100); // falsche Anzahl, nur 1 Param.
zeichneGesicht(50, "hallo"); // falsche Anzahl, falscher Typ

So ist es richtig:

zeichneGesicht(0);
zeichneGesicht2(255, 0);

Beachten Sie, dass Sie beim Funktionsaufruf natürlich auch Variablen in den Parametern verwenden können, z.B.

int farbe1 = 0;
int farbe2 = 255;

zeichneGesicht(farbe1);
zeichneGesicht2(farbe1, farbe2);

Fingerübungen

a) Plus eins

Schreiben Sie die Funktion plusEins, die als Parameter eine Zahl x als int bekommt und x+1 auf die Konsole schreibt.

Der Aufruf plusEins(5) würde also folgendes produzieren:

6
Lösung
void plusEins(int x) {
  println(x + 1);
}

Übungsaufgaben

7.2 a) Summe ausgeben   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion printSum, die drei Parameter vom Typ float bekommt und die Summe dieser drei übergebenen Zahlen auf die Konsole schreibt.

Der Aufruf printSum(3, 2, 5) würde also folgendes produzieren:

10.0

7.2 b) Sterne sehen   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion sternchen, die als Parameter eine ganze Zahl n bekommt und dann n Sternchen auf der Konsole ausgibt.

Der Aufruf sternchen(5) würde folgendes ausgeben:

*****

Hinweis: Verwenden Sie eine Schleife.

7.2 c) Mauszeiger mit Parametern   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion mauszeiger mit fünf Parametern: x, y, r, g, b. Die Funktion zeichnet einen Kreis an der Position (x, y) mit der Farbe (r, g, b) gefüllt.

Testen Sie Ihren Code mit

void draw() {
  background(255);
  mauszeiger(mouseX, mouseY, 255, 0, 0);
}

Sie sollten einen rot gefüllten Kreis sehen, der der Maus folgt.

7.2 d) Begrüßung   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion greeting, die einen Parameter name vom Typ String bekommt und dann "Servus, <name>" ausgibt.

Zum Beispiel beim Aufruf von

greeting("Helmut");

Sehen Sie:

Servus, Helmut

7.2 e) Begrüßung mit Gender   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine neue Funktion greet, diesmal mit zwei Parametern, einen String und einen boolean. Der zweite Parameter gibt an, ob die Person weiblich ist.

Beim Aufruf

greet("Schmidt", true);

soll kommen:

Hallo Frau Schmidt

Bei

greet("Huber", false);

soll kommen:

Hallo Herr Huber

Zusammenfassung

Sie können einer Funktion Parameter geben - das sind Informationen, die das Verhalten der Funktion bestimmen, z.B. Farbwerte oder Koordinaten.

In der Definition der Funktion geben Sie den Parametern Namen und nennen den Typ. Im Prinzip deklarieren Sie hier eine Reihe von lokalen Variablen:

void NAME(TYP1 PARAM1, TYP2 PARAM2, ...) {
  CODE
}

Beim Aufruf einer Funktion müssen Sie die konkreten Parameterwerte angeben (hierbei dürfen natürlich auch Variablen im Spiel sein). Dabei muss die Anzahl, Reihenfolge und Datentypen der Parameter mit den Angaben Ihrer Funktionsdefinition übereinstimmten.

Sie kennen Funktionsaufrufe zur Genüge - jede Zeile des folgenden Codes ruft eine Funktion auf, die jeweils von Processing selbst zur Verfügung gestellt wurde.

size(200, 200);
background(0);
fill(255);
ellipse(50, 40, 20, 20);
println("fertig");

Häufiger Fehler ist, dass bei der Definition einer Funktion die Typen vergessen werden oder dass umgekehrt beim Aufruf einer Funktion der Typ mit angegeben wird.

7.3 Rückgabewert

Sie können nicht nur einer Funktion Infos mit auf den Weg geben, sondern Sie können auch eine Info (und zwar genau eine) zurückbekommen. Das ist der Rückgabewert.

Video: Funktionen 3 - Rückgabewert (9:57)

Rückgabetyp definieren

Nehmen wir an, Sie wollen eine komplexe Rechnung mit einer Funktion abhandeln, z.B. die Qudratzahl einer Zahl zu berechnen. Dann definieren Sie:

float quadratzahl(float x) {
   float erg = x * x;
   return erg;
}

Sie sehen hier zwei neue Dinge. Erstens steht da statt "void" jetzt "float". Zweitens sehen Sie das Wort "return".

Das float bedeutet, dass diese Funktion einen Wert vom Typ float zurückgibt. Wohingegen void (engl. für "nichts") bedeutet, dass eine Funktion keinen Wert zurückgibt.

Damit Sie Processing sagen können, was genau zurückgegeben werden soll (und wann), gibt es das Schlüsselwort return . Im Beispiel definieren Sie eine neue lokale Variable namens erg. Diese Variable ist nur gültig bis zur nächsten schließenden geschweiften Klammer (auf ihrer Ebene). Sobald Processing auf das Wort return trifft, wird der Wert von erg zurückgegeben und es werden keine weiteren Zeilen mehr verarbeitet.

Das heißt, jeglicher Code unterhalb von return ist sinnlos:

float quadratzahl(float x) {
   float erg = x * x;
   return erg; // Processing gibt Wert zurück und bricht ab
   println("bin ich jetzt dran?"); // wird nie ausgeführt
}

Tipp: Hinter return können auch ganze Ausdrücke stehen, d.h. in kurz wäre es so:

float quadratzahl(float x) {
   return x * x;
}

Vielleicht noch ein zweites Code-Beispiel, das etwas interessanter ist. Sie möchten testen, ob ein Punkt (px, py) innerhalb eines Rechtecks (rx, ry, rw, rh) liegt (die vier Werte definieren Eckpunkt, Breite und Höhe), z.B. um eine Kollision in einem Spiel zu gestalten. Sie definieren dazu eine Funktion, die das testet und wahr/falsch zurückgibt, also einen boolean :

boolean imRechteck(int px, int py, int rx, int ry,
                   int rw, int rh)
{
   if (px > rx && px < rx + rw && py > ry && py < ry + rh) {
      return true;
   } else {
      return false;
   }
}

Wieder der Tipp, dass Sie hinter return auch Ausdrücke stellen können. Die If-Bedingung ist nichts anderes als ein boolescher Ausdruck, der zu true oder false ausgewertet wird. Das heißt, Sie können direkt schreiben:

boolean imRechteck(int px, int py, int rx, int ry,
                   int rw, int rh)
{
   return (px > rx && px < rx + rw && py > ry && py < ry + rh);
}

Und jetzt schauen wir uns an, wie Sie die Funktionen aufrufen.

Return garantieren

Sie müssen bei einer Funktion mit Rückgabewert sicher stellen, dass in jedem Fall ein return erreicht wird.

Das wird dann wichtig, wenn Sie z.B. ein if im Code haben, denn hier werden ja mitunter Code-Teile übersprungen, d.h. Ihr return wird vielleicht nie erreicht.

Schauen wir uns eine einfache Funktion an, die prüft, ob eine Zahl positiv ist:

boolean isPositive(int num) {
  if (num >= 0) {
    return true;
  }
}

Processing weigert sich, diesen Code auszuführen, denn es ist nicht garantiert, dass das return erreicht ist. Was, wenn die Zahl negtiv ist?

Den Mangel beheben wir entweder so:

boolean isPositive(int num) {
  if (num >= 0) {
    return true;
  } else {
    return false;
  }
}

Oder so:

// Alterantive Lösung ohne Else

boolean isPositive(int num) {
  if (num >= 0) {
    return true;
  }
  return false;
}

In beiden Fällen wird immer ein return erreicht.

Rückgabewerte im Code verwenden

Beim Aufruf einer Funktion mit Rückgabewert wird der Aufruf durch den Rückgabewert ersetzt. Insofern ähnelt dies dem Prinzip einer Variable, die durch ihren Inhalt ersetzt wird.

Aus

println(quadratzahl(5));

wird im nächsten Schritt also

println(25);

Rückgabewerte werden häufig zunächst an Variablen gebunden und dann weiter verarbeitet:

float z = quadratzahl(5);
println("Ergebnis: " + z);

Sie können aber den Funktionsaufruf auch direkt als Parameter für eine andere Funktion (hier wäre das println) einbinden:

println("Ergebnis: " + quadratzahl(5));

Hier ein Beispiel mit der zweiten Funktion:

boolean collision = imRechteck(25, 40, 20, 30, 100, 100);
if (collision) {
   println("kaputt!");
}

Auch hier könnten Sie den Funktionsaufruf direkt in die If-Bedingung hineinsetzen.

if (imRechteck(25, 40, 20, 30, 100, 100)) {
   println("kaputt!");
}

Allgemein sollten Sie einen Funktionsaufruf mit Rückgabewert genauso betrachten wie eine Variable. Eine Variable vom Typ int kann überall auftreten, wo ein int -Wert stehen könnte. Genauso kann ein Funktionsaufruf mit Rückgabetyp int überall dort stehen, wo ein int -Wert stehen könnte.

Beachten die syntaktische Ähnlichkeit von Variablendeklaration und Funktionsdefinition:

float foo;

float quadratzahl(float x) {
   return x * x;
}

Beide Konstrukte, Variable und Funktion stehen stellvertretend für einen Wert von einem bestimmten Typ. Daher können beide an den gleichen Stellen stehen.

Fingerübungen

a) Einfache Funktion

Schreiben Sie eine Funktion foo, die immer den Wert 42 zurückgibt. Die Funktion hat keine Parameter und den Rückgabtyp int.

Testen Sie Ihre Funktion mit

void setup() {
  int num = foo();
  println(num);
}

Auch dies sollte funktionieren:

void setup() {
  println(foo());
}

Sie sollten sehen:

42
Lösung
int foo() {
  return 42;
}

b) Addieren

Schreiben Sie eine Funktion plus, die zwei Zahlen vom Typ float als Parameter hat und die Summe der Zahlen (ebenfalls float) zurückgibt.

Testen Sie Ihre Funktion mit:

void setup() {
  println(plus(10, 20));
	println(plus(5, -3));
}
30.0
2.0
Lösung
float plus(float a, float b) {
  float c = a + b;
  return c;
}

Es geht auch kürzer:

float plus(float a, float b) {
  return a + b;
}

Übungsaufgaben

7.3 a) Intervall-Check   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion bereich, die drei Zahlen a, b, c vom Typ float als Parameter hat. Die Rückgabe ist eine boolescher Wert: true, wenn a zwischen b und c liegt und false sonst.

Gehen Sie davon aus, dass b immer kleiner als c ist.

Testen Sie Ihre Funktion mit:

void setup() {
    println(bereich(10, 0, 100));
    println(bereich(-10, 0, 100));
}

Sie sollten sehen:

true
false

7.3 b) Quadrat   Level 11 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion quadrat mit einem Parameter des Typs float. Die Funktion gibt die Quadratzahl des Parameters zurück.

Testen Sie Ihre Funktion mit:

void setup() {
    println(quadrat(3));
    println(quadrat(5));
}

Sie sollten sehen:

9.0
25.0

Verwenden Sie nicht die Processing-Funktion pow.

7.3 c) Maximum   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion max2, die zwei float-Werte bekommt und den größeren der beiden zurückgibt.

Schreiben Sie die Funktion max3, die drei float-Werte bekommt und den größten zurückgibt. Testen Sie unbedingt auch Fälle mit zwei gleichen Werten wie (5, 5, 1) oder (5, 1, 5).

Verwenden Sie nicht die Processing-Funktion max.

Tipp
Sie können max3 zwar mit "if" lösen, es gibt aber auch eine sehr elegante (und kurze) Lösung, wo die Funktion max2 verwendet wird. Sie bekommen dies sogar auf einer einzigen Zeile unter.

7.3 d) Abstand   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion difference, die zwei float-Werte bekommt und den Abstand zwischen den beiden Werten als postive Zahl zurückgibt.

Testen Sie Ihre Funktion mit:

void setup() {
  println(difference(5.5, 10.5));
  println(difference(1.5, 0));
}

Sie sollten sehen:

5.0
1.5

Verwenden Sie nicht die Processing-Funktion abs.

Tipp
Sie müssen unterscheiden, ob der erste Wert größer als der zweite ist - oder umgekehrt.

7.3 e) Gerade-ungerade   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion istGerade, die eine ganze Zahl bekommt und einen Wahrheitswert zurückgibt, nämlich true, wenn die Zahl gerade ist, false wenn ungerade.

7.3 f) Primzahl   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion istPrim, die eine ganze Zahl bekommt und einen Wahrheitswert zurückgibt, nämlich true, wenn die Zahl eine Primzahl ist, false sonst.

Eine Primzahl ist eine Zahl, die nur durch 1 und durch sich selbst teilbar ist. Zum Beispiel ist die 7 eine Primzahl, da sie nur durch 1 und 7 teilbar ist. Die 8 ist keine Primzahl, da sie durch 1, 2, 4 und 8 teilbar ist.

Testen Sie Ihren Code mit:

void setup() {
  println(istPrim(7));
  println(istPrim(15));
  println(istPrim(97));
}
true
false
true

Zusammenfassung

Funktionen können einen Wert zurückgeben und somit ihrer Benennung als "Funktion" gerecht werden, dann auch mathematische Funktionen erzeugen einen Wert, wenn man ihnen einen Parameter übergibt.

Bei der Definition einer Funktion mit Rückgabewert muss man den Typ des Rückgabewerts angeben. Dieser Typ steht vor dem Funktionsnamen.

float quadratzahl(float x) {
  // ...
}

Im Code der Funktion muss jeder mögliche Verlauf in einer Zeile enden, die ein return ausführt. Hier wird spezifiziert, welcher Wert zurückgeliefert wird. Der Typ des Werts muss natürlich mit dem Typ übereinstimmen, der in der Kopfzeile angegeben ist.

float quadratzahl(float x) {
  float erg = x * x;
  return erg;
}

Häufiger Fehler ist, dass bei Verwendung von z.B. if vergessen wird, dass der Code verschiedene Wege nehmen kann.

float never42(float x) {
  if (x != 42) {
    return x;
  }
  // FEHLER: kein return für den Fall x == 42
}

Beim Aufruf einer Funktion mit Rückgabewert wird der Aufruf durch den Rückgabewert ersetzt ähnlich einer Variable, deren Aufruf durch ihren Wert ersetzt wird.

7.4 Funktionen und Arrays

Ein Array kann auch einer Funktion als Parameter übergeben werden. Wenn Sie z.B. die Summe eines int-Arrays berechnen wollen:

int summe(int[] zahlen) {
    int result = 0;
    for (int i = 0; i < zahlen.length; i++) {
        result += zahlen[i];
    }
    return result;
}

Wie Sie im Beispiel sehen, behandeln Sie int[] wie einen ganz normalen Datentypen.

Sie können einen Array auch als Rückgabetyp verwenden. Nehmen wir an, Sie wollen einen int-Array mit einer bestimmten Länge erzeugen, der als Voreinstellung die gleiche Zahl in allen Zellen hat:

int[] arrayMitVoreinstellung(int laenge, int zahl) {
    int[] result = new int[laenge];
    for (int i = 0; i < laenge; i++) {
        result[i] = zahl;
    }
    return result;
}

Sie behandeln auch hier den Array-Typen int[] wie einen anderen Typ. Das gleiche gilt natürlich für float-Arrays, String-Arrays etc.

Arrays sind Objekte

Besondere Vorsicht ist geboten, wenn Sie einen Array übergeben (Parameter) und den Array in der Funktion verändern. Da der Array ein Objekt ist und also nur ein Mal existiert, verändern Sie mit der Funktion diesen einen Array.

Schauen Sie sich folgenden Code an. Was passiert mit a?

void setup() {
  int[] a = {1, 2, 3};
  doSomething(a);
  println(a);
}

void doSomething(int[] arr) {
  arr[0] = 42;
}

Hier wird Array a übergeben an doSomething. Die Parametervariable arr zeigt natürlich auf den selben Array wie a. Bei diesem einen Array wird das erste Element auf 42 gesetzt. Dabei ist es egal, ob Sie über a oder arr auf diesen Array zugreifen. Folglich erhalten Sie:

[0] 42
[1] 2
[2] 3

Beachten Sie, dass das gleiche Beispiel nicht bei primitiven Datentypen funktioniert, was hoffentlich auch Ihrer Erwartung entspricht:

void setup() {
  int x = 0;
  doSomething(x);
  println(x);
}

void doSomething(int foo) {
  foo = 42;
}

Hier wird der Wert von x in die Parametervarialbe foo kopiert. Die Tatsache, dass innerhalb der Funktion das foo geändert wird, hat keinerlei Konsequenzen für x. Daher sehen Sie:

0

Natürlich ist es erlaubt, einen Array innerhalb einer Funktion zu ändern. Nur muss man sich dessen sehr bewusst sein, wenn man die Funktion aufruft. Eine Alternative ist es oft, in der Funktion einen neuen Array zu erzeugen, den übergebenen Array dort hinein zu kopieren, an der Kopie die Änderung vorzunehmen und diesen neuen Array zurückzugeben. In diesem Fall wird allerdings mehr Speicherplatz verbraucht (zwei Arrays statt einem), also muss man im Einzelfall entscheiden, was sinnvoller ist.

Fingerübungen

a) Array ausgeben

Schreiben Sie eine Funktion ausgeben, die einen int-Array als Parameter bekommt und den Inhalt des Arrays in exakt folgendem Format auf der Konsole ausgibt:

Index 0 enthält 101
Index 1 enthält 120
Index 2 enthält -500

Testen Sie Ihren Code mit:

void setup() {
  int[] test1 = { 1, 2, 3 };
  int[] test2 = { 101, 120, -500 };
  ausgeben(test1);
  ausgeben(test2);
}
Lösung
void ausgeben(int[] a) {
  for (int i = 0; i < a.length; i++) {
    println("Index " + i + " enthält " + a[i]);
  }
}

b) Array erzeugen

Schreiben Sie eine Funktion makeIntArray, die eine ganze Zahl n als Parameter bekommt und einen neuen Integer-Array der Länge n zurückgibt.

Testen Sie Ihren Code mit:

void setup() {
  println(makeIntArray(5));
}
[0] 0
[1] 0
[2] 0
[3] 0
[4] 0
Lösung
int[] makeIntArray(int n) {
  int[] a = new int[n];
  return a;
}

Man kann es auch ganz kompakt so schreiben:

int[] makeIntArray(int n) {
  return new int[n];
}

Übungsaufgaben

Achten Sie in allen folgenden Aufgaben darauf, dass Sie keine globalen Variablen innerhalb der zu definierenden Funktion verwenden.

7.4 a) Durchschnitt   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion average, die einen float-Array als Parameter bekommt und den Durchschnitt der dort enthaltenen Zahlen berechnet und zurückgibt (float).

Testen Sie Ihren Code mit:

void setup() {
  float[] test = { 1.5, 3.5, 1 };
  println(average(test));
}

Testen Sie die Robustheit Ihres Codes, indem Sie den Testarray um ein paar Zahlen erweitern. Ihr Code sollte weiterhin funktionieren.

7.4 b) Minimum   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion arrayMin, die einen float-Array als Parameter bekommt und den kleinsten Wert der dort enthaltenen Zahlen findet und zurückgibt (float).

Zum Beispiel sollten Sie für den Array { 5, -13, 32.5 } den Wert -13 zurückbekommen.

Tipp
Offensichtlich brauchen Sie hier mal wieder eine Schleife. Verwenden Sie zudem eine Varible, um den "aktuell" (in dem jeweiligen Schleifendurchlauf) minimalsten Wert zu speichern und aktualsieren Sie diese Variable in jeder Runde (wenn nötig).

7.4 c) Erste/letzte negative Zahl   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie zwei Funktionen.

Die erste Funktion firstNegative bekommt einen float-Array als Parameter und gibt die erste negative Zahl der dort enthaltenen Zahlen zurück.

Zum Beispiel sollten Sie für den Array { 5, -13, 32.5, -5.1, 0 } den Wert -13 zurückbekommen.

Die zweite Funktion lastNegative bekommt auch einen float-Array als Parameter und gibt die letzte negative Zahl der dort enthaltenen Zahlen zurück.

Zum Beispiel sollten Sie für den Array { 5, -13, 32.5, -5.1, 0 } den Wert -5.1 zurückbekommen.

Tipp
Muss man jeweils den ganzen Array durchlaufen? In welcher Reihenfolge geht man am besten durch den Array, insbes. bei lastNegative?

Testen Sie Ihre Funktionen mit:

void setup() {
  float[] test = { 5, -13, 32.5, -5.1, 0 };
  println(firstNegative(test));
  println(lastNegative(test));
}

7.4 d) Array erzeugen   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion makeArray, die zwei Parameter bekommt - length und number - und einen int-Array zurückgibt. Der zurückgegebene Array soll die Länge length haben und in jeder Zelle den Wert number enthalten.

Beispiel: Mit dem Aufruf makeArray(3, 10) sollten Sie den Array {10, 10, 10} zurückbekommen.

7.4 e) Zufallsarray   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion createRandomArray, die einen float-Array mit Zufallszahlen (zwischen 0 und 1) enthält. Die Länge "anzahl" (int) wird als Parameter übergeben.

7.4 f) Zahlenarray erzeugen   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion create123, die einen int-Array zurückgibt, der die Zahlen 1, 2, 3, 4, ... bis zu einer Länge "anzahl" enthält. Die Länge "anzahl" (int) wird als Parameter übergeben.

7.4 g) Zahlenarray mit Start und Länge   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion numbers, die einen int-Array zurückgibt, die zwei Parameter bekommt: eine Startzahl (start) und eine Länge (len). Die Funktion soll einen Array zurückgeben, der mit der Zahl start beginnt, dann die entsprechenden aufeinander folgenden Zahlen enthält und so lang ist wie len.

Zum Beispiel bei start=5 und len=3 müsste { 5, 6, 7 } zurückgegeben werden.

Variante: Schreiben Sie außerdem die Funktion numbers2, die zwei Parameter bekommt: start und end. Sie gibt einen Array zurück, der mit der Zahl start beginnt und mit der Zahl end endet. Zum Beispiel bei start=3 und end=6 müsste { 3, 4, 5, 6 } zurückgegeben werden.

7.4 h) Array filtern   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion filterArrayPositive, die einen int-Array numbers bekommt und einen int-Array zurückgibt. Der zurückgegebene Array enthält nur die positiven Zahlen von numbers.

Beispiel: Wenn der übergebene Array {1, -1, -20, 15} wäre, müsste der Array {1, 15} zurückgegeben werden.

Tipp
Gehen Sie in zwei Schritten vor, indem Sie zuerst herausfinden, wieviele Elemente der Ergebnisarray haben soll. Dann erzeugen Sie das Ergebnisarray und befüllen diesen (achten Sie hier auf die Indices).

7.4 i) Klausur-Check   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie eine Funktion klausurCheck, die testet, ob ein Klausurteilnehmer die notwendige Punktzahl von 50 erreicht und somit bestanden hat.

Die Funktion bekommt einen Array mit Namen und einen Array mit Klausurergebnissen (Punkte). Geben Sie aus, welche Person bestanden hat.

Testen Sie Ihren Code mit:

void setup() {
  String[] namen = {"Schmidt", "Holz", "Hauser", "Eberhardt", "Drexler"};
  int[] punkte = {50, 3, 44, 98, 80};
  klausurCheck(namen, punkte);
}

Nennen Sie in Ihrer Funktion die zwei Parameter anders als namen und punkte. Das ist zwar nicht zwingend notwendig, aber es ist konzeptionell verwirrend, wenn die Variablen und Parameter gleich heißen.

Sie sollten sehen:

Schmidt hat mit 50 Punkten bestanden.
Eberhardt hat mit 98 Punkten bestanden.
Drexler hat mit 80 Punkten bestanden.

Als Zusatzaufgabe zählen Sie noch diejenigen, die durchgefallen sind und geben Sie die Zahl mit aus (letzte Zeile):

Schmidt hat mit 50 Punkten bestanden.
Eberhardt hat mit 98 Punkten bestanden.
Drexler hat mit 80 Punkten bestanden.
2 Teilnehmer sind durchgefallen.

7.4 j) Arrayelemente tauschen   Level 31 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Sie möchten in einem Array zwei Elemente tauschen, also z.B. das Element mit Index 1 mit dem Element mit Index 4.

Schreiben Sie die Funktion swap, die drei Parameter hat: eine int-Array und zwei Indexzahlen (int). Die Funktion tauscht die Werte der beiden Elemente, also swap(a, 1, 4) würde bei dem Array a = {1, 2, 3, 4, 5} die Werte 2 und 5 tauschen.

Testen Sie Ihren Code mit

void setup() {
  int[] a = {1, 2, 3, 4, 5};
  swap(a, 1, 4);
  println(a);
}

Sie sollten sehen:

[0] 1
[1] 5
[2] 3
[3] 4
[4] 2

Natürlich sollte Ihre Funktion für beliebige Indexzahlen funktionieren.

7.4 k) Array einfügen   Level 41 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion insertArray, die zwei Arrays a und b und eine Zahl pos bekommt.

Die Funktion fügt Array b in Array a ein und zwar an Position pos. Sie gibt den resultierenden (neuen) Array zurück.

Testen Sie Ihren Code mit

void setup() {
  int[] a = { 1, 2, 3, 4, 5 };
  int[] b = { 101, 102, 103 };
  println(insertArray(a, b, 2));
}

Sie sollten sehen:

[0] 1
[1] 2
[2] 101
[3] 102
[4] 103
[5] 3
[6] 4
[7] 5
Tipp
Erzeugen Sie zunächst den Ergebnisarray und befüllen Sie ihn in drei Schritten (welche sind das?). Nach jedem dieser Schritte können Sie erstmal testen, ob dieser Teil funktioniert. Sie müssen etwas mit den Indices jonglieren. Vielleicht hilft eine Skizze auf Papier?

7.4 l) Negative Werte entfernen   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie die Funktion noNegatives, die eine int-Array bekommt und in dem Array alle negativen Zahlen durch Null ersetzt.

Testen Sie Ihren Code mit:

void setup() {
  int[] a = { 1, 5, -10, 20, -3 };
  noNegatives(a);
  println(a);
}
[0] 1
[1] 5
[2] 0
[3] 20
[4] 0

7.4 m) One-Hot-Encoding als Funktion   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Im Bereich des Deep Learning muss man Zahlen wie 2 oder 4 mit jeweils einen Vektor darstellen. Man wandelt diese Zahlen dann in Vektoren um (z.B. mit Länge 5), die überall eine Null haben außer an der entsprechenden Stelle, wo eine 1 steht. Die Zahl 2 wird also zum Vektor (0 0 1 0 0) und die Zahl 4 zu (0 0 0 0 1).

Schreiben Sie eine Funktion onehot, die zwei Zahlen z und len bekommt. len ist die Länge des Vektors (Arrays) und z die Zahl die enkodiert werden soll. Die Funktion soll einen Array entsprechend der Kodierung zurückgeben.

7.5 Überladen und Signatur

Processing/Java erlaubt es, mehrere Funktionen mit dem selben Namen zu definieren. Diese Eigenschaft einer Programmiersprache nennt man auch Überladen (overloading), weil man quasi einen einzigen Namen mit mehreren Funktionen belädt.

Hier haben wir z.B. zwei verschiedene Funktionen, die beide foo heißen:

// Beispiel 1: Zwei Funktionen, gleicher Name

void setup() {
  foo(1);
  foo(1, 2);
}

void foo(int a) {
  println(a);
}

void foo(int x, int y) {
  println(x + y);
}
1
3

Woher weiß Processing, welche Funktion jeweils genommen werden soll? In dem Beispiel könnte man meinen, dass Processing auf die Anzahl der Parameter schaut. Das ist richtig, ist aber nur die halbe Wahrheit. Man kann nämlich auch zwei Funktionen mit der gleichen Anzahl von Parametern definieren:

// Beispiel 2: Gleiche Anzahl von Parametern

void setup() {
  foo(1, 2);
  foo(true, false);
}

void foo(int x, int y) {
  println("Addition: " + (x + y));
}

void foo(boolean x, boolean y) {
  println("Die Wahrheit ist: " + (x && y));
}

Als Ausgabe bekommen wir:

Addition: 3
Die Wahrheit ist: false

Das heißt, auch hier wählt Processing "die richtigen" Funktionen aus, indem Processing auf die Datentypen der Parameter schaut (int im ersten Fall, boolean im zweiten Fall).

Wie sieht es mit folgendem Beispiel aus?

// Beispiel 3: Gleiche Anzahl und Typen der Parameter

void setup() {
  foo(42, "hallo");
  foo("hi", 18);
}

void foo(int x, String s) {
  println(s + ":" + x);
}

void foo(String s, int x) {
  println(s + "#" + x);
}

Wir haben zwei Funktionen, die beide gleich heißen, die gleiche Anzahl von Parametern haben und die gleichen Typen. Und doch funktioniert der Code:

hallo:42
hi#18

Der Grund ist: die Reihenfolge der Parametertypen ist unterschiedlich. Das erste foo hat erst int, dann String. Das zweite foo hat erst String, dann int.

Signatur

Allgemein sagt man, dass Processing die Signatur einer Funktion zu Rate zieht.

Signatur

Die Signatur einer Funktion besteht aus den folgenden Informationen:

  • Name der Funktion
  • Anzahl, Reihenfolge und Typen aller Parameter

Nicht zur Signatur gehören die Namen der Parameter. Auch der Rückgabewert gehört nicht zur Signatur.

Im den oberen Beispielen hätte man folgende Signaturen.

Beispiel 1:

foo(int)
foo(int, int)

Beispiel 2:

foo(int, int)
foo(boolean, boolean)

Beispiel 3:

foo(int, String)
foo(String, int)

Sie bemerken vielleicht zwei Dinge: (1) alle Signaturen sind unterschiedlich und (2) die Variablennamen der Parameter tauchen gar nicht auf, sind also irrelevant.

Wenn man die jeweilige Signatur einer Funktion hinschreibt, sieht man also sofort, ob man die jeweilige Funktion definieren darf oder nicht.

Die Regel lautet: Man darf nur Funktionen definieren, die unterschiedliche Signaturen haben.

Dies Beispiel ist demnach auch korrekt:

// Überladen von foo mit jeweils einem Parameter

void setup() {
  foo(true);
  foo(32);
}

// Signatur ist foo(int)

void foo(int x) {
  println(10 + x);
}

// Signatur ist foo(boolean)

void foo(boolean b) {
  println("Wahrheitswert: " + b);
}
Wahrheitswert: true
42

Wie gesagt spielt es keine Rolle, wie Ihre Parameter heißen, also ob Sie schreiben foo(boolean b) oder foo(boolean blabla). Für die Signatur ist nur der Typ des jeweiligen Parameters relevant.

Beachten Sie, dass der Rückgabetyp nicht zur Signatur gehört. Dass bedeutet, Sie dürfen nicht zwei Funktionen mit gleicher Signatur, aber unterschiedlichem Rückgabetyp definieren:

// So geht das nicht!

void setup() {
  foo(true, false); // hier wird Fehler gemeldet
}

// Signatur ist foo(boolean, boolean)
void foo(boolean x, boolean y) {
  println("Die Wahrheit ist: " + (x && y));
}

// Signatur ist auch foo(boolean, boolean)
// Daher der Fehler!
boolean foo(boolean x, boolean y) {
  return x && y;
}

Übungsaufgaben

7.5 a) Begrüßung mit Varianten   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie mehrere Varianten der Funktion greet, so dass folgender Code funktioniert:

void setup() {
  greet("Schmidt");
  greet("Schmdit", true);
  greet("Schmidt", false, "Dr.");
}

Der boolesche Parameter beim zweiten und dritten Aufruf bezieht sich auf das Geschlecht der Person (true = weiblich).

Die Ausgabe sollte wie folgt aussehen:

Guten Tag, Frau oder Herr Schmidt
Guten Tag, Frau Schmdit
Guten Tag, Herr Dr. Schmidt

7.5 b) Summe   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

Schreiben Sie zwei Varianten der Funktion add, so dass sowohl int-Zahlen als auch Vektoren addiert werden können. Geben Sie die entsprechende Zahl bzw. den Vektor zurück. Achten Sie bei der Vektoraddition darauf, dass die übergebenen Vektoren nicht verändert werden. Testen Sie Ihr Programm mit:

void setup() {
  int summe = add(10, -5);

  PVector v1 = new PVector(1, 1);
  PVector v2 = new PVector(100, 50);
  PVector summeVek = add(v1, v2);

  println("Summe Zahlen = " + summe);
  println("Vektor 1 = " + v1);
  println("Vector 2 = " + v2);
  println("Summe Vektoren = " + summeVek);
}

Sie sollten sehen:

Summe Zahlen = 5
Vektor 1 = [ 1.0, 1.0, 0.0 ]
Vector 2 = [ 100.0, 50.0, 0.0 ]
Summe Vektoren = [ 101.0, 51.0, 0.0 ]

Zusammenfassung

  • Man kann mehrere Funktionen mit dem selben Namen definieren. Das nennt man Überladen.
  • Die Signatur einer Funktion besteht aus dem Namen und der Liste der Parametertypen (wobei die Reihenfolge wesentlich ist)
  • Man darf nur Funktionen definieren, die unterschiedliche Signaturen haben

7.6 Skopus von Variablen

Sie kennen bereits den Begriff Skopus aus Kapitel 2.5, wo wir den Unterschied zwischen globalen Variablen (oben im Code deklariert) und lokalen Variablen (innerhalb eines Code-Blocks deklariert) besprochen haben. Hier sehen wir uns den Skopus nochmal am Beispiel einer Funktion an.

Lokale Variablen sind immer nur im eigenen Code-Block gültig, ab der Zeile, in der sie deklariert werden. Im folgenden sehen wir die verschiedenen Gültigkeitsbereiche von einer globalen Variablen und zwei lokalen Variablen innerhalb ihrer jeweiligen Code-Blöcke. Der Skopus der globalen Variable x ist das gesamte Programm, inklusive der Funktion checkBounds.

Hier nochmal der Code zum Ausprobieren:

// Bring die Maus zwischen Ball und linker Wand,
// dann ändert sich die Farbe

int x = 0;

void draw() {
  background(255);
  checkBounds();
  ellipse(x, height/2, 20, 20);
  x++;
}

void checkBounds() {
  int middle = x / 2;

  if (mouseX > middle - 5 && mouseX < middle + 5) {
    float ran = random(1);
    fill(ran * 255);
  }

  if (x > width) {
    x = 0;
  }
}

Übungsaufgaben

7.6 a) Finde den Skopus   Level 21 = easy
2 = relativ leicht
3 = mittel
4 = schwierig
5 = hart

In welchen Bereichen gelten die Variablen x, num und s? Überlegen Sie zuerst und merken Sie sich Ihre Entscheidung. Überprüfen Sie dann Ihre Überlegung durch Einkommentieren der entsprechenden Print-Anweisungen.

int x = 0;

void setup() {
  int num = 20;
  size(200, 200);
  // println(num);
  // println(s);
  // println(x);
}

void draw() {
  ellipse(x, 50, 20, 20);
  if (x > width) {
    String s = "hello";
    x = 0;
    // println(num);
    // println(s);
    // println(x);
  }
  // println(num);
  // println(s);
  // println(x);
}