Station - Datenverarbeitung mit Funktionen

Beispiel 1

Wir betrachten die Berechnung des BMI-Werts.

Der Body-Mass-Index (kurz: BMI) ist eine Zahl, mit der man abschätzen kann, ob man Unter-, Normal oder Übergewicht hat. Man berechnet diese Zahl nach der folgenden Formel:

          Gewicht
BMI = ---------------
       Größe * Größe

Dabei wird das Gewicht in kg und die Größe in m angegeben. Wenn jemand beispielsweise 70 kg wiegt und 1.70 m groß ist, dann berechnet man den BMI-Wert wie folgt: 70/(1.70*1.70) = 24.22.

Ein einfaches imperatives Programm zur Berechnung des BMI-Werts könnte so aussehen:

# Eingabe
gewicht = float(input("Gewicht in kg: "))
groesse = float(input("Größe in m: "))
# Verarbeitung
bmi = gewicht / (groesse * groesse)
# Ausgabe
print("BMI:", bmi)

Eine Ausführung des Programms könnte folgende Ausgaben im Python-Ausführfenster erzeugen:

>>> 
Gewicht in kg: 70
Größe in m: 1.70
BMI: 24.2214532872

Wenn man die Berechnung des BMI-Werts funktional modelliert, lässt man erst einmal die Eingabe und Ausgabe der Daten weg und konzentriert sich nur auf die Verarbeitung der Daten:

def bmi(gewicht, groesse):
    return gewicht / (groesse * groesse)

Durch einen Funktionsaufruf wird dann die Datenverarbeitung für übergebene Daten tatsächlich ausgeführt. Das Ergebnis der Datenverarbeitung wird abschließend zurückgeliefert.

>>> bmi(70, 1.70)
24.221453287197235

Wir ersetzen also die Eingabe von Daten (durch einen Benutzer) durch die abstraktere Datenübergabe und die Ausgabe der Daten (auf dem Bildschirm) durch die abstraktere Datenrückgabe.

Beispiel 2

Wir betrachten die Berechnung des Anhaltewegs eines Autos (vgl. Einstieg - Benutzeroberflächen).

In der Fahrschule lernt man folgende Faustformeln zur Berechnung des Anhaltewegs eines Autos:

Zunächst wieder ein einfaches imperatives Programm:

# Eingabe
geschwindigkeit = float(input("Geschwindigkeit [in km/h] : "))
# Verarbeitung
reaktionsweg = (geschwindigkeit/10)*3
bremsweg = (geschwindigkeit/10)**2
anhalteweg = reaktionsweg + bremsweg
# Ausgabe
print("Reaktionsweg [in m]: ", reaktionsweg)
print("Bremsweg [in m]: ", bremsweg)
print("Anhalteweg [in m]: ", anhalteweg)

Es liefert z.B. den folgenden Dialog mit einem Benutzer:

>>> 
Geschwindigkeit [in km/h] : 80
Reaktionsweg [in m]:  24.0
Bremsweg [in m]:  64.0
Anhalteweg [in m]:  88.0

Ein entsprechendes funktionales Programm beschreibt die Verarbeitung der Daten:

def wegberechnungen(geschwindigkeit):
    return ((geschwindigkeit/10)*3,
            (geschwindigkeit/10)**2,
            (geschwindigkeit/10)*3 + (geschwindigkeit/10)**2)

Ein Funktionsaufruf aktiviert dann die Verarbeitung konkret übergebener Daten.

>>> wegberechnungen(80)
(24.0, 64.0, 88.0)

Funktionale Programme

Eine Funktion ist eine Einheit, die übergebene Daten verarbeitet und den berechneten Funktionswert als Ergebnis zurückgibt. Die Verarbeitung wird über eine Funktionsdefinition (man sagt oft auch Funktionsdeklaration) festgelegt. Aktiviert wird eine Verarbeitung durch einen Funktionsaufruf.

Eine Funktionsdefinition leistet folgende Festlegungen: die Festlegung der Datenübergabe mit Hilfe von Parameter, die Festlegung der Datenverarbeitung und die Festlegung der Datenrückgabe.

Bei einem Funktionsaufruf werden die Parameter mit konkreten Werten versehen. Diese Daten werden dann nach den Festlegungen in der Funktionsdefinition verarbeitet. Abschließend wird ein Funktionswert als Ergebnis zurückgeliefert.

Ein funktionales Programm besteht aus einer oder mehrerer Funktionsdefinitionen und einem Funktionsaufruf.

Funktionale Programme mit lokalen Bindungen

In einem rein funktionalen Programm dürfen in den Funktionsdefinitionen außer den Parametern keine weiteren Variablen vorkommen.

In der Funktionsdefinition kommen dann auch keine Zuweisungen vor. Hierdurch wird gewährleistet, dass keine Seiteneffekte zustande kommen können.

Die oben gezeigten Programme sind in diesem Sinne rein funktionale Programme.

Beispiel 2 lässt bereits eine erste kleine Schwierigkeit erkennen. Teilausdrücke wie (geschwindigkeit/10)*3 und (geschwindigkeit/10)**2 kommen mehrfach in der Berechnungsvorschrift vor und müssen bei der Ausführung gegebenenfalls mehrfach berechnet werden. Das ist ineffizient. Einfacher wäre es, wenn Teilausdrücke nur einmal berechnt werden müssten und die Werte dann weiterverarbeitet werden könnten. Um das zu ermöglichen, braucht man Variablen:

def wegberechnungen(geschwindigkeit):
    reaktionsweg = (geschwindigkeit/10)*3
    bremsweg = (geschwindigkeit/10)**2
    anhalteweg = reaktionsweg + bremsweg
    return (reaktionsweg, bremsweg, anhalteweg)

Damit verstoßen wir jedoch gegen die Bedingung, (außer Parametern) keine Variablen zu benutzen.

Ein Ausweg ergibt sich hier, wenn wir Variablen nur als lokale Konstanten verwenden. Das bedeutet, dass Variablen nur lokal innerhalb von Funktionsdefinitionen vorkommen dürfen und dass ihnen nur ein einziges mal ein Wert zugewiesen werden darf. Auf der rechten Seite einer Zuweisung darf dann die Variable, die auf der linken Seite der Zuweisung steht, nicht vorkommen. Durch die Zuweisung erhalten die in dieser Weise eingeführten Variablen einmal einen Wert, der anschließend nicht mehr verändert wird. Man kann so jederzeit eine Variable durch den Term auf der rechten Seite der zur Variablenfestlegung gehörenden Zuweisung ersetzen. Man spricht in diesem Zusammenhang auch von lokalen Bindungen. Ein Term wird hier lokal (d.h. innerhalb der Funktionsdefinition) an einen Namen gebunden, so dass man diesen Namen an Stelle des Terms benutzen kann. Funktionale Programme mit lokalen Bindungen kann man also jederzeit in streng funktionale Programme umwandeln, ohne das Verhalten des Programms zu verändern. Evtl. wird nur die Berechnungszeit hierdurch beeinflusst. Funktionale Programme mit lokalen Bindungen erzeugen also keine Seiteneffekte und sind - genau wie rein funktionale Programme - referentiell transparent.

Funktionale Programmierung und das Modularisierungsprinzip

Funktionale Programmierung setzt konsequent das Modularisierungsprinzip um. Modularisierung bedeutet, ein Gesamtsystem nach dem Baukastenprinzip aus Einzelbausteinen (den sogenannten Modulen) zusammenzusetzen. Funktionen können als Bausteine angesehen werden. Wenn sie - wie in der funktionalen Programmierung üblich - frei von Seiteneffekten konzipiert werden, dann können die Bausteine unabhängig und flexibel genutzt werden.

Zu klären ist noch, wie man Funktionen (als Bausteine betrachtet) zusammensetzten kann. Das soll in den nächsten Abschnitten geklärt werden.

X

Fehler melden

X

Suche