Exkurs - Von der strukturierten zur funktionalen Programmierung
Seiteneffekte
Wenn eine Funktion den Wert einer globalen Variablen verändert, liegt ein Seiteneffekt vor.
Ein Aufruf der folgenden Funktion addiereZaehler
führt zu einem Seiteneffekt,
da der Wert der globalen Variable zaehler
dabei verändert wird.
zaehler = 0 def addiereZaehler(x): global zaehler zaehler = zaehler + 1 x = x + zaehler return x # Test print(zaehler) print(addiereZaehler(2)) print(zaehler)
Entsprechendes gilt für den Aufruf der Funktion invertiereBild
(s.u.), sofern
der Parameter L
mit einer globalen Variablen aktualisiert wird. Beide Variablen
- der Parameter L
und die zur Aktualisierung benutzte globale Variablen -
verwalten dann dieselbe Liste.
def invertiereBild(L): """ das in L dargestellte Bild wird invertiert d.h.: aus 0 wird 1 und aus 1 wird 0 """ for i in range(3, len(L)): if L[i] == 0: L[i] = 1 else: L[i] = 0 return L # Test bild = ['P1', 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1] print(bild) negativ = invertiereBild(bild) print(negativ) print(bild) negativ = invertiereBild(bild) print(negativ) print(bild)
Seiteneffekte führen dazu, dass das Verhalten von Funktionen schwer zu durchschauen ist. Insbesondere bei komplexeren Programmen verliert man leicht den Überblick, welche (beabsichtigten und auch unbeabsichtigten) Nebenwirkungen ein Funktionsaufruf mit Seiteneffekten hat. Man versucht daher, Seiteneffekte möglichst zu vermeiden.
Eine Möglichkeit besteht darin, die Verantwortung für seiteneffektfreie Programme dem Programmentwickler zu überlassen. Dieser muss dann dafür Sorge tragen, dass keine Bausteine mit Seiteneffekten erstellt werden.
Eine andere Möglichkeit besteht darin, die Programmiersprache so zu konzipieren, dass keine Seiteneffekte mehr möglich sind.
Funktionale Programmierung ist (zumindest in der strengen Version) so konzipiert, dass keine Seiteneffekte auftreten können.
Referentielle Transparenz
Seiteneffekte können dazu führen, dass gleiche Ausdrücke unterschiedliche Werte haben.
Im folgenden Programm taucht im Ausdruck tausche(getZaehler(), getZaehler())
zweimal im Teilausdruck getZaehler()
auf.
from zaehler import * def tausche(x, y): return (y, x) # Test print(tausche(2, 3)) print(tausche(1, 1)) print(getZaehler()) print(tausche(getZaehler(), getZaehler()))
Normalerweise würde man erwarten, dass derselbe Teilausdruck getZaehler()
auch immer denselben Wert besitzt. Im Programm oben ist das aber nicht der Fall, da es bei der
Auswertung der Teilausdrücke zu Seiteneffekten kommt, die den Wert der Teilausdrücke beeinflussen.
Ein solches Verhalten führt leicht zu Fehlern, da man sehr genau auf die Reihenfolge der Auswertung der Teilausdrücke achten muss. Man versucht daher, solche Abhängigkeit zu vermeiden.
Referentielle Transparenz liegt vor, wenn ein Teilausdruck - unabhängig von seiner Position innerhalb eines Ausdrucks und unabhängig vom Zeitpunkt der Auswertung - immer denselben Wert hat.
Funktionale Programmierung ist so konzipiert, dass referentielle Transparenz stets gewährleistet ist.
Ein Dialog über seiteneffektfreie Programmierung
Hallo! Ich vertrete hier die imperative Programmierung.
Hallo, und ich vertrete die funktionale Programmierung.
Stimmt es, dass du keine Seiteneffekte erlaubst?
Ja, so ist es.
Und wie machst du das?
Seiteneffekte entstehen durch Zuweisungen an globale Variablen.
Bei mir gibt es gar keine Zuweisungen mehr. Dann kann man auch nicht in die Verlegenheit
kommen, einer globalen Variablen etwas zuzuweisen.
Klingt einfach. Aber, wie soll man dann einen so einfachen Vorgang wie den folgenden
ohne Zuweisungen beschreiben?
ALGORITHMUS Summenberechnung:
{vorher: n ∈ N}
setze s auf 0
setze i auf 0
SOLANGE i <= n:
erhöhe s um i
erhöhe i um 1
{nachher: s = 0+1+2+...+n}
Man muss es halt ganz anders machen.
Das kann ich mir nicht so recht vorstellen.
Wenn es keine Zuweisungen gibt, dann macht es auch keinen großen Sinn, Variablen
einzuführen. Man hat ja keine Möglichkeit, den Wert der Variablen zu verändern.
Und wenn man Werte von Variablen nicht verändern kann, dann kann man es durch
Anweisungen im Schleifenkörper auch nicht erreichen, dass die Abbruchbedingung
einer SOLANGE-Schleife erfüllt wird. Das macht ja alles keinen Sinn mehr.
Wieso bist du so auf Variablen und Schleifen fixiert?
Variablen benötigt man ja wohl zur Datenverwaltung und ohne Schleifen gibt es keine Wiederholungen.
Oder sehe ich das ganz falsch?
Ja, das siehst du etwas zu eng. Wenn du wissen willst, wie mein Berechnungskonzept funktioniert,
dann musst du dir den nächsten Abschnitt anschauen.