Programmieren III - Funktionen

Die Einführungsfolien finden sie hier für Funktionen und Rekursion.

Code Re-Use

Mit steigender Code-Komplexität ist es oft notwendig, dass die gleiche Funktionalität an mehreren Stellen im Code benötigt wird. Um dies zu erreichen könnten wir den Code einfach mehrmals kopieren und an den jeweiligen Stellen einfügen. Das führt jedoch zu einer großen Menge an Redundanz im Code, was es wesentlich schwerer macht, den Code zu verstehen, anzupassen oder etwaige Fehler auszubessern. Die Lösung für dieses Problem sind sogenannte Funktionen.

Eine Funktion ist ein Codeblock, welcher ein oder mehrere Statements enhält und diese unter einem eindeutigen Namen für die Wiederverwendung bereitstellt. Erstellen sie eine neue Datei einfache_funktion.py und fügen sie folgenden Code ein:

def hallo(name):
    print('Hallo ' + name)

Der Code demonstriert die Grundkonzepte einer Funktion. Zuerst haben wir das def Schlüsselwort, welches eine neue Funktion definiert. Danach kommt der Funktionsnamen, welcher später dazu verwendet werden wird, um auf die Funktion zuzugreifen. Nach dem Funktionsnamen kommen in runden Klammern die Funktionsparameter. Funktionsparameter sind Variablen, welche es möglich machen Daten von ausserhalb der Funktion in die Funktion hinein zu übergeben. Ein Doppelpunkt schließt die Funktionsdefinition ab und der Code der Funktion findet sich dann eingerückt in den Zeilen danach. In diese Beispiel definieren wir eine Funktion hallo, welche einen einzelnen Parameter erwartet (name). Wenn die Funktion später aufgerufen wird, gibt sie dann den Wert der Variable name plus den Text 'Hallo ' aus.

Wenn sie den Code jetzt ausführen, werden sie sehen, dass nichts passiert. Das ist weil die Definition einer Funktion nicht automatisch dazu führt, dass der Code ausgeführt wird. Um das zu erreichen, müssen wir die Funktion explizit aufrufen. Aktualisieren sie dazu den Code, damit er wie folgt aussieht:

def hallo(name):
    print('Hallo ' + name)

hallo('Mark')

Wenn sie den Code jetzt ausführen, werden sie sehen, dass der Text "Hallo Mark" ausgegeben wird. Das ist weil wir jetzt die Funktion aufrufen, indem wir ihren Namen angeben und danach in runden Klammern die Parameterwerte. In diesem Fall ist der Parameterwert 'Mark', welcher dann in der Funktion in der Variable name verfügbar ist und daher dann auch ausgegeben wird. Ändern sie den Parameterwert nach Belieben und führen sie den Code erneut aus um zu sehen, was der Effekt ist.

Der volle Nutzen von Funktionen wird aber erst sichtbar, wenn wir sie wiederholt aufrufen. Aktualisieren sie dazu den Code wie folgt:

def hallo(name):
    print('Hallo ' + name)

hallo('Mark')
hallo('Sophie')

Wenn sie den Code jetzt ausführen, dann wird zuerst der Text "Hallo Mark" und dann der Text "Hallo Sophie" ausgegeben. Das ist natürlich ein relativ einfaches Beispiel, aber er demonstriert das Grundprinzip.

Rückgabewerte

Wenn wir nur Daten ausgeben wollen, dann ist eine Funktion wie oben ausreichend. In den meisten Fällen wollen wir jedoch in der Funktion ein oder mehrere Aktionen ausführen und das Ergebnis dann zurückgeben, damit es an einer anderen Stelle im Code weiterverwendet werden kann. Erstellen sie dazu eine neue Datei rueckgabewerte.py und fügen sie folgenden Code ein:

def das_doppelte(zahl):
    return zahl * 2

start = 6
doppel = das_doppelte(start)
print(doppel)

Wenn sie den Code jetzt ausführen, werden sie sehen, dass der Wert 12 ausgegeben wird. Das wird über das return Statement erreicht. Das return Statement hat zuerst das return Schlüsselwort und danach einen Ausdruck, welcher definiert, was zurückgegeben wird. In diesem Fall wird zuerst die Variable zahl mit 2 multipliziert und dann das Ergebnis zurückgegeben.

Dort wo wir die Funktion aufrufen, können wir jetzt das Ergebnis der Funktion einer Variablen zuweisen (oder in einer weiteren Funktion weiterverwenden). Hier speichern wir das Ergebnis in der Variable doppel und geben diese dann in der nächsten Zeile aus. Verändern sie jetzt den Wert der start Variable und beobachten sie die Auswirkungen.

Im Beispiel weisen wir das Ergebnis des Funktionsaufrufes einer Variablen zu und geben diese dann aus. Wir können das ganze aber auch etwas kompakter schreiben:

def das_doppelte(zahl):
    return zahl * 2

start = 6
doppel = das_doppelte(start)
print(doppel)

print(das_doppelte(start))

Wenn sie den Code jetzt ausführen, werden sie sehen, dass zweimal die gleiche Zahl ausgegeben wird. Der einzige Unterschied ist, dass für die zweite Ausgabe, wir keine Variable nutzen, sondern das Ergebnis des Aufrufes der das_doppelte Funktion direkt als den Parameter für die print Funktion nutzen. Wenn sie Code schreiben, sollten sie im ersten Augenblick darauf abzielen, dass der Code funktioniert. Im zweiten Schritt zahlt es sich dann aber oft aus, den Code zu vereinfachen. Dies macht ihn generell lesbarer und dadurch wartbarer.

Standardwerte

Manchmal wollen wir für unsere Funktion Parameter definieren, welche einen Standardwert haben. Erstellen sie dazu eine neue Datei standardwert.py und fügen sie folgenden Code ein:

def begruessung(name, anrede='Hallo'):
    print(anrede + ' ' + name)

begruessung('Mark')
begruessung('Sophie', anrede='Guten Tag')

Unsere neue Funktion begruessung hat zwei Parameter (name und anrede getrennt durch einen Beistric), wobei wir in der Funktionsdefinition dem Parameter anrede direkt einen Wert zuweisen. Dies ist der Standardwert für diesen Parameter. Wenn wir beim Funktionsaufruf keinen Wert für einen Parameter angeben, welcher einen Standardwert hat, dann erhält in der Funktion die Variable des Parameters den Standardwert.

Wir sehen das im ersten Aufruf der Funktion, wo wir keine anrede übergeben und wo daher der Text "Hallo Mark" ausgegeben wird. Anders läuft es im zweiten Beispiel ab, wo wir den Wert 'Guten Tag' für den Parameter anrede übergeben und daher dann der Text "Guten Tag Sophie" ausgegeben wird. Es ist zwar nicht zwingend notwendig, aber normalerweise werden, wie im Beispiel gezeigt, Parameter mit Standardwerten explizit mit parameter=wert übergeben.

In unserem Code kombinieren wir drei Strings zusammen, um den Text auszugeben. Das funktioniert so zwar, ist aber nicht wie in Python idealerweise Strings kombiniert werden. Aktualisieren sie daher den Code, damit er wie folgt aussieht:

def begruessung(name, anrede='Hallo'):
    print('{anrede} {name}'.format(name=name, anrede=anrede))

begruessung('Mark')
begruessung('Sophie', anrede='Guten Tag')

Die Zeile nutzt jetzt die Python Stringformattierungssyntax. Zuerst definieren wir den Formattierungsstring '{anrede} {name}'. In diesem werden mit den '{xxxx}' Ausdrücken Platzhalter definiert. Dann, wenn wir die format Funktion des Formattierungsstrings aufrufen, müssen wir für jeden Platzhalter einen Parameter mit der gleichen, expliziten Angabe des Parameternamens übergeben. Die format Funktion ersetzt dann die Platzhalter mit den jeweils übergebenen Werten. Wichtig ist nur, dass Platzhalter und Parametername exact übereinstimmen müssen.

Rekursion

Das letzte Funktionskonzept, welches wir betrachten werden, ist die Rekursion. Rekursion ist ein etwas komplexeres, aber sehr mächtiges Instrument um den Code einfacher zu machen. Grundsätzlich ist in der Rekursion die Idee, dass eine Funktion sich selbst aufruft. Erstellen sie eine neue Datei rekursion.py und fügen sie folgenden Code hinzu:

def print_list(elemente):
    if elemente:
        print(elemente[0])
        print_list(elemente[1:])

print_list(['To', 'be', 'or', 'not', 'to', 'be'])

Führen sie den Code aus und sie werden sehen, dass jedes Wort ein einer eigenen Zeile ausgegeben wird. Dies funktioniert, weil in unserer rekursiven print_list Funktion, wir zuerst überprüfen, ob die Liste noch Elemente enthällt (if elemente - eine Liste mit mindestens einem Element ist immer true, eine mit null Elemente false). Wenn noch mindestens ein Element da ist, dann geben wir das Element aus (print(elemente[0])). Danach rufen wir die print_list Funktion erneut auf, nur dass wir jetzt nur einen Teil der elemente Liste übergeben und zwar den Teil der Liste, welcher an der Stelle 1 (also das zweite Element) und alle darauf folgenden Elemente enthält (elemente[1:]).

Damit eine rekursive Funktion korrekt funktioniert müssen immer zwei Bedingungen gegeben sein:

  • Der rekursive Funktionsaufruf muss innerhalb eines if Statements sein, damit es einen Fall geben kann, wo die Funktion nicht erneut aufgerufen wird.
  • Beim rekursiven Aufruf müssen die Parameter so geändert werden, dass irgendwann das if Statement nicht mehr wahr ist und die Funktion nicht mehr rekursiv aufgerufen wird.

Wenn sie diese Bedingungen einhalten, können sie erfolgreich rekursive Funktionen schreiben. Falls sie einen Fehler machen und die Rekursion wird unendlich, dann können sie das Programm abbrechen, indem sie Strg + C auf der Tastatur drücken.

Übung

Probieren sie die folgenden Übungsaufgaben selbstständig aus. Erstellen sie dazu ein neues Verzeichnis "uebungen_3" und legen darin für jede Aufgabe eine eigene Datei an.

Aufgabe 1:

Erstellen sie eine neue Datei aufgabe_1.py und schreiben sie den notwendigen Code um eine neue Funktion addition zu definieren, welche zwei Parameter nimmt, diese zusammenaddiert und dann das Ergebnis zurückliefert.

Aufgabe 2:

Erstellen sie eine neue Datei aufgabe_2.py und schreiben sie den notwendigen Code um eine neue Funktion grossbuchstabe zu definieren, welche überprüft, um ein String mit einem Großbuchstaben beginnt. Die Funktion solle True zurückliefern, wenn der String mit einem Großbuchstaben beginnt und False wenn nicht.

Aufgabe 3:

Erstellen sie eine neue Datei aufgabe_3.py und schreiben sie den notwendigen Code um eine neue Funktion drei_gewinnt zu definieren, welche einen Parameter nimmt und das dreifache des Parameterwertes zurückliefert. Nutzen sie die Funktion um aus einer Variable a mit dem Wert 3, eine zweite Variable mit dem Wert 27 zu berechnen.

Aufgabe 4:

Erstellen sie eine neue Datei aufgabe_4.py und schreiben sie den notwendigen Code um eine neue Funktion text_note zu definieren, welche für einen Notenwertparameter, den Notentext zurückliefert. Die Rückgabewerte sollten wie folgt sein: "Sehr gut", wenn die note den Wert 100 oder 130 hat, "Gut" wenn die note den Wert 170, 200 oder 230 hat, "Befriedigend" wenn die note den Wert 270, 300 oder 330 hat, "Genügend" wenn die note den Wert 370, 400 oder 430 hat, und "Unbefriedigend" für alle anderen Werte.

Aufgabe 5:

Erstellen sie eine neue Datei aufgabe_5.py und schreiben sie den notwendigen Code um eine neue Funktion umkehren zu definieren, welche als Parameter eine Liste nimmt und diese dann in umgekehrter Reihenfolge zurückliefert.

Aufgabe 6:

Erstellen sie eine neue Datei aufgabe_6.py und schreiben sie den notwendigen Code um eine neue Funktion paritaet zu definieren, welche zwei Parameter hat. Der erste Parameter ist eine Zahl, der zweite ein String, welcher den Standardwert "gerade" hat. Wenn der zweite Parameter den Wert "gerade" hat, dann soll die Funktion True zurückliefern, wenn der erste Parameter eine gerade Zahl ist (0, 2, 4, 6, ...) und sonst False. Wenn der zweite Parameter "ungerade" ist, dann soll die Funktion True zurückliefern, wenn der erste Parameter eine ungerade Zahl ist (1, 3, 5, 7, ...) und sonst False.

Aufgabe 7:

Erstellen sie eine neue Datei aufgabe_7.py und schreiben sie den notwendigen Code um eine neue, rekursive Funktion umkehren zu definieren, welche als Parameter eine Liste nimmt und diese dann in umgekehrter Reihenfolge zurückliefert.

Aufgabe 8:

Erstellen sie eine neue Datei aufgabe_8.py und schreiben sie den notwendigen Code um eine neue, rekursive Funktion countdown zu definieren, welche die Zahlen von 1 bis 10 ausgibt.

Aufgabe 9:

Erstellen sie eine neue Datei aufgabe_9.py und schreiben sie den notwendigen Code um eine neue, rekursive Funktion umkehren zu definieren, welche als Parameter eine Liste nimmt und diese dann in umgekehrter Reihenfolge zurückliefert.

Aufgabe 10:

Erstellen sie eine neue Datei aufgabe_10.py und schreiben sie den notwendigen Code ein, um mittels einer rekursiven Funktion die ersten 10 Zahlen der Fibonacci Sequenz zu berechnen. In der Fibonacci Sequenz hat das erste Element den Wert 1, das zweite Element auch den Wert 1, und alle darauffolgenden Werte sind die Summe der zweit letzten Werte (z.B. das dritte Element ist die Summe des ersten und zweiten Elements, als 1 + 1 = 2).