ppt

Werbung
Funktionale Programmierung
Klaus Becker
2014
2
Funktionale Programmierung
3
Teil 1
Warum funktional programmieren?
4
Ausgangspunkt – die Maschine
Bei der Entwicklung von Programmiersprachen stand zu Beginn die Steuerung der realen
Hardware im Mittelpunkt. Mit einem überschaubaren Befehlssatz konnte man direkt die
benutzte „Hardware-Maschine“ programmieren.
org 100h
start:
mov dx,meldung1
mov ah,9h
int 021h
mov ah, 01h
;Wert über die Tastatur einlesen
int 021h
cmp al, '5'
jne ende
mov dx,meldung2
mov ah,9h
int 021h
ende:
mov ah,4Ch
int 21h
section .data
meldung1: db 'Bitte geben Sie eine Zahl ein:', 13, 10, '$'
meldung2: db 13, 10, 'Sie haben die Zahl 5 eingegeben.', 13, 10, '$'
Wir orientieren uns im Folgenden nicht an realer Hardware, sonderrn benutzen mit der
Registermaschine ein einfaches Maschinenmodell.
Registermaschine
5
Registermaschinen enthalten einen Speicher, der aus einzelnen, durchnummerierten Registern
besteht, in denen natürliche Zahlen gespeichert werden können.
0
5
1
3
2
0
...
...
> 0
1
2
3
4
5
6
TST
JMP
JMP
DEC
INC
JMP
HLT
1
3
6
1
0
0
Zur Steuerung der Registermaschinen gibt es eine einfache maschinennahe
Programmiersprache, die nur die folgenden 5 Befehle kennt:
> x INC i
Erhöhe Register i um 1. Gehe zu Zeile x+1.
> x DEC i
Erniedrige Register i um 1. Gehe zu Zeile x+1.
> x JMP i
Gehe zu Zeile i.
> x TST i
Wenn Register i ungleich 0 ist,
dann gehe zu Zeile x+1, sonst zu Zeile x+2.
> x HLT
Beende die Bearbeitung.
6
Programmanalyse
Aufgabe
(a) Kannst du auf den ersten Blick erkennen, was das gezeigte
Registermaschinenprogramm leistet?
(b) Führe das Programm aus. Gehe von der folgenden Anfangsbelegung der
Register aus:
{R0: 0; R1: 4; R2:6; R3:0; ...}.
Weißt du jetzt, was das Programm leistet? Wenn nicht, dann führe weitere
Tests durch.
(c) Warum ist es so schwer zu erkennen, was das Programm leistet?
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TST
JMP
JMP
TST
JMP
JMP
TST
JMP
HLT
TST
JMP
HLT
DEC
DEC
INC
JMP
DEC
JMP
DEC
JMP
1
3
6
2
12
9
2
18
1
16
1
2
0
0
1
9
2
6
Spaghetti-Code
7
Maschinennahe Programmiersprachen nutzen Sprungbefehle (wie z.B. JMP), um
Wiederholungen zu realisieren.
Sprungbefehle können
leicht dazu führen, dass
Programme völlig
undurchschaubar
werden. Im gezeigten
Programm führen die
Sprungbefehle zu einer
Art Spaghetti-Code, bei
dem die Ablauflogik
nicht leicht zu entwirren
ist.
Ablaufmodellierung
mit Sprungbefehlen
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TST
JMP
JMP
TST
JMP
JMP
TST
JMP
HLT
TST
JMP
HLT
DEC
DEC
INC
JMP
DEC
JMP
DEC
JMP
1
3
6
2
12
9
2
18
1
16
1
2
0
0
1
9
2
6
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
TST
JMP
JMP
TST
JMP
JMP
TST
JMP
HLT
TST
JMP
HLT
DEC
DEC
INC
JMP
DEC
JMP
DEC
JMP
1
3
6
2
12
9
2
18
1
16
1
2
0
0
1
9
2
6
Spaghetti-Code
8
Verzicht auf Sprungbefehle
Schon früh erkannte man die Schwäche von Sprungbefehlen und versucht seither, auf höheren
Programmierebenen möglichst auf die Verwendung solcher Befehle zu verzichten.
Strukturierte Programmierung benutzt keine Sprungbefehle zur Ablaufmodellierung, sondern
Kontrollstrukturen, die bestimmte Ablaufmuster vorgeben.
while (R1 != 0) and (R2 != 0):
R0 = R0 + 1
R1 = R1 - 1
R2 = R2 - 1
while R1 != 0:
R1 = R1 - 1
while R2 != 0:
R2 = R2 - 1
Ablaufmodellierung
mit Kontrollstrukturen
Im Programm wird die Kontrollstruktur "Wiederholung mit Eintrittsbedingung" (dargestellt
durch eine while-Schleife) zur Modellierung benutzt.
Kontrollstrukturen erhöhen die Transparenz der Ablauflogik und verringern die Fehlerquote bei
der Entwicklung von Programmen.
9
Prozeduren
Als Weiterentwicklung von hardwareorientierten Programiersprachen können die prozeduralen
Programmiersprachen angesehen werden. Hier können Sequenzen von Anweisungen zu
Prozeduren zusammengefasst werden, die dann als Befehle für eine abstraktere, hypothetische
Maschine gedeutet werden können.
program bsp
!
implicit none
real :: x, y, z
write( *, * ) 'x -> '
read( *, * ) x
call sub( x, y, z )
write( *, * ) 'y -> ', y, 'z -> ', z
end
subroutine sub( a, b, c )
!
implicit none
real :: a, b, c
b = a**2
c = a**2 + b
end subroutine sub
10
Programmanalyse
Aufgabe
(a) Was leistet die Prozedur minimum?
(b) Ergänze den Python-Dialog.
(c) Was ist an der gezeigten Vorgehensweise problematisch?
def minimum(a, b):
global m
while (a != 0) and (b != 0):
m = m + 1
a = a - 1
b = b - 1
>>> m = 0
>>> minimum(5, 8)
>>> m
>>> minimum(7, 2)
>>> m
11
Seiteneffekte
Das Unterprogramm auf der linken Seite verändert den Wert einer globalen Variablen. Das
Unterprogramm hat einen sog. Seiteneffekt.
def minimum(a, b):
global m
while (a != 0) and (b != 0):
m = m + 1
a = a - 1
b = b - 1
>>>
>>>
>>>
5
>>>
>>>
7
m = 0
minimum(5, 8)
m
minimum(7, 2)
m
Unterprogramm mit Seiteneffekt
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.
12
Vermeidung von Seiteneffekten
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 und dass referentielle Transparenz stets gewährleistet ist.
13
Referentielle Transparenz
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.
def minimum(a, b):
global m
while (a != 0) and (b != 0):
m = m + 1
a = a - 1
b = b – 1
Unterprogramm mit Seiteneffekt
>>>
>>>
>>>
5
>>>
>>>
10
m = 0
minimum(5, 8)
m
def minimum(a, b):
m = 0
while (a != 0) and (b != 0):
m = m + 1
a = a - 1
b = b - 1
return m
Funktion ohne Seiteneffekt
>>> minimum(5, 8)
5
>>> minimum(5, 8)
5
minimum(5, 8)
m
keine referentielle Transparenz
14
Dialog über Seiteneffekte
I: Hallo! Ich vertrete hier die imperative Programmierung.
F: Hallo, und ich vertrete die funktionale Programmierung.
I: Stimmt es, dass du keine Seiteneffekte erlaubst?
F: Ja, so ist es.
I: Und wie machst du das?
F: 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.
I: Klingt einfach. Aber, wie soll man dann einen so einfachen
Vorgang wie den folgenden ohne Zuweisungen beschreiben?
F: Man muss es halt ganz anders machen.
ALGORITHMUS Summenberechnung:
{vorher: n aus 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}
I: 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.
F: Wieso bist du so auf Variablen und Schleifen fixiert?
I: Variablen benötigt man ja wohl zur Datenverwaltung und ohne Schleifen gibt es keine Wiederholungen. Oder sehe ich das
ganz falsch?
F: Ja, das siehst du etwas zu eng. Wenn du wissen willst, wie mein Berechnungskonzept funktioniert, dann musst du dir den
nächsten Abschnitt anschauen.
15
Exkurs: Seiteneffekte bei Objekten
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
def addBit(i, j):
"""
die Bits i, j werden add.:
0+0->0;0+1->1;1+0->1;1+1->1
"""
s = i+j
if s == 2:
s = 1
return s
def addBilder(M, N):
"""
die Bitfolgen in M und N
werden Bit für Bit addiert
"""
L = []
for i in range(len(M)):
if i < 3:
L = L + [M[i]]
else:
L = L +
[addBit(M[i], N[i])]
return L
16
Exkurs: Seiteneffekte bei Objekten
>>> bild = ['P1', 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1]
>>> negativ = invertiereBild(bild)
>>> negativ
['P1', 3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0]
>>> neuesBild = addBilder(bild, negativ)
>>> neuesBild
???
Aufgabe:
Was erwartest du als Ergebnis beim Testdialog? Warum erhält man ein anderes Ergebnis?
17
Teil 2
Funktionen als Programmierbausteine
18
Datenverarbeitung mit Funktionen
Beispiel: BMI-Berechnung
Eingabe der Daten
# Eingabe
gewicht = float(input("..: "))
groesse = float(input("..: "))
# Verarbeitung
bmi = gewicht / (groesse *
groesse)
# Ausgabe
print("BMI:", bmi)
Ausgabe der Daten
>>>
Gewicht in kg: 70
Größe in m: 1.70
BMI: 24.2214532872
Übergabe der Daten
def bmi(gewicht, groesse):
return \
gewicht/(groesse*groesse)
Rückgabe der Daten
Wir ersetzen 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 und konzentrieren uns
stärker auf die Verarbeitung der Daten.
>>> bmi(70, 1.70)
24.221453287197235
19
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.
Ein funktionales Programm besteht aus
einer oder mehrerer Funktionsdefinitionen
und einem Funktionsaufruf.
Funktiondefinition
def bmi(gewicht, groesse):
return \
gewicht/(groesse*groesse)
>>> bmi(70, 1.70)
24.221453287197235
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.
20
Funktionale Programme
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.
def wegberechnungen(geschwindigkeit):
return ((geschwindigkeit/10)*3,
(geschwindigkeit/10)**2,
(geschwindigkeit/10)*3 + (geschwindigkeit/10)**2)
rein funktionales Programm
Schwierigkeit:
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.
21
Lokale Bindungen
def wegberechnungen(geschwindigkeit):
keine (neuen) Variablen
return ((geschwindigkeit/10)*3,
(geschwindigkeit/10)**2,
(geschwindigkeit/10)*3 + (geschwindigkeit/10)**2)
def wegberechnungen(geschwindigkeit):
reaktionsweg = (geschwindigkeit/10)*3
bremsweg = (geschwindigkeit/10)**2
anhalteweg = reaktionsweg + bremsweg
return (reaktionsweg, bremsweg, anhalteweg)
nur lokale Bindungen
Ein Ausweg ergibt sich, wenn wir Variablen 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. Man spricht in diesem Zusammenhang auch von lokalen Bindungen.
22
Lokale Bindungen
def wegberechnungen(geschwindigkeit):
keine (neuen) Variablen
return ((geschwindigkeit/10)*3,
(geschwindigkeit/10)**2,
(geschwindigkeit/10)*3 + (geschwindigkeit/10)**2)
def wegberechnungen(geschwindigkeit):
reaktionsweg = (geschwindigkeit/10)*3
bremsweg = (geschwindigkeit/10)**2
anhalteweg = reaktionsweg + bremsweg
return (reaktionsweg, bremsweg, anhalteweg)
nur lokale Bindungen
Mit lokalen Bindungen wird ein Term 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.
23
Funktionskomposition
Beispiel: erstes Zeichen eines Wortes ans Ende verschieben
# Eingabe
wort = input('Wort: ')
# Verarbeitung
# entferne das erste Zeichen
erstesZeichen = wort[0]
wort = wort[1:]
# füge es am Ende des Worts ein
wort = wort + erstesZeichen
# Ausgabe
print(wort)
def erstesZeichen(wort):
return wort[0]
def ohneErstesZeichen(wort):
return wort[1:]
def mitLetztemZeichen(wort, \
zeichen):
return wort + zeichen
mehrere Aktionen hintereinander
ausführen
Funktionsaufrufe ineinander schachteln
>>> mitLetztemZeichen(ohneErstesZeichen('TOR'), erstesZeichen('TOR'))
'OR'
'ORT'
'T'
24
Funktionskomposition
Bei der Komposition
Beispiel:
Zeichen innerhalb
bzw. Verkettung
eines Wortes
vonverschieben
Funktionen werden Funktionen
ineinandergeschachtelt aufgerufen.
def erstesZeichen(wort):
return wort[0]
def ohneErstesZeichen(wort):
return wort[1:]
def mitLetztemZeichen(wort, zeichen):
return wort + zeichen
def erstesZeichenAlsLetztesZeichen(wort):
return mitLetztemZeichen(ohneErstesZeichen(wort), \
erstesZeichen(wort))
Aufgabe: Löse die Probleme in (a) und (b) analog mit einer Funktion, die geeignete
Hilfsfunktionen benutzt.
(a) In einer nicht-leeren Zeichenkette soll das letzte Element ganz an an den Anfang der
Zeichenkette gesetzt werden. Aus 'ORT' soll so 'TOR' erzeugt werden.
(b) In einer nicht-leeren Zeichenkette soll das erste mit dem letzten Zeichen ausgetauscht
werden. Aus 'TOR' soll so 'ROT' erzeugt werden.
25
Fallunterscheidungen
Bei einer Funktionsdefinition mit Fallunterscheidung werden die Funktionswerte für
verschiedene Fälle (evtl. unterschiedlich) festgelegt.
Beispiel: erstes Zeichen eines Wortes entfernen
WENN das Wort Zeichen enthält:
entferne das erste Zeichen
SONST:
tue nichts
def ohneErstesZeichen(wort):
if wort != '':
return wort[1:]
else:
return ''
Fallunterscheidung
Funktionsdefinition mit
Fallunterscheidung
Aufgabe: Löse die folgenden Probleme mit geeigneten Hilfsfunktionen. Dabei soll auch
berücksichtigt werden, dass das Ausgangswort eventuell keine Zeichen enthält.
(a) In einer nicht-leeren Zeichenkette soll das letzte Element ganz an an den Anfang der
Zeichenkette gesetzt werden. Aus 'ORT' soll so 'TOR' erzeugt werden.
(b) In einer nicht-leeren Zeichenkette soll das erste mit dem letzten Zeichen ausgetauscht
werden. Aus 'TOR' soll so 'ROT' erzeugt werden.
26
Wiederholungen
Beispiel: Wörter umdrehen
{neuesWort -> ''; altesWort -> 'LEBEN'}
neuesWort = altesWort[0] + neuesWort
altesWort = altesWort[1:]
{neuesWort -> 'L'; altesWort -> 'EBEN'}
neuesWort = altesWort[0] + neuesWort
altesWort = altesWort[1:]
{neuesWort -> 'EL'; altesWort -> 'BEN'}
neuesWort = altesWort[0] + neuesWort
altesWort = altesWort[1:]
{neuesWort -> 'BEL'; altesWort -> 'EN'}
neuesWort = altesWort[0] + neuesWort
altesWort = altesWort[1:]
{neuesWort -> 'EBEL'; altesWort -> 'N'}
neuesWort = altesWort[0] + neuesWort
altesWort = altesWort[1:]
{neuesWort -> 'NEBEL'; altesWort -> ''}
iterativer Lösungsansatz
(benutzt Variablen und eine Schleife)
Eingabe: wort
altesWort = wort
neuesWort = ''
SOLANGE altesWort noch Zeichen enthält:
füge das erste Zeichen von altesWort vorne in neuesWort ein
entferne das erste Zeichen von altesWort
Ausgabe: neuesWort
27
Rekursion
Beispiel: Wörter umdrehen
umdrehen('LEBEN') ->
(umdrehen('EBEN') + 'L') ->
((umdrehen('BEN') + 'E') + 'L') ->
(((umdrehen('EN') + 'B') + 'E') + 'L') ->
((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') ->
(((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') ->
((((('' + 'N') + 'E') + 'B') + 'E') + 'L') ->
(((('N' + 'E') + 'B') + 'E') + 'L') ->
((('NE' + 'B') + 'E') + 'L') ->
(('NEB' + 'E') + 'L') ->
('NEBE' + 'L') ->
'NEBEL'
rekursiver Lösungsansatz
(benutzt Problemreduktionen und
Funktionskomposition)
Rekursion
28
Beispiel: Wörter umdrehen
Problemreduktionsmuster:
umdrehen('LEBEN') ->
(umdrehen('EBEN') + 'L') ->
((umdrehen('BEN') + 'E') + 'L') ->
(((umdrehen('EN') + 'B') + 'E') + 'L') ->
((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') ->
(((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') ->
((((('' + 'N') + 'E') + 'B') + 'E') + 'L') ->
(((('N' + 'E') + 'B') + 'E') + 'L') ->
((('NE' + 'B') + 'E') + 'L') ->
(('NEB' + 'E') + 'L') ->
Fall 1: wort == ''
umdrehen(wort) -> wort
Fall 2: wort != ''
umdrehen(wort) -> umdrehen(wort[1:]) + wort[0]
('NEBE' + 'L') ->
'NEBEL'
rekursive Funktionsdefinition
def umdrehen(wort):
if len(wort) == 0:
return ''
else:
return umdrehen(wort[1:]) + wort[0]
29
Rekursion
Rekursive Problemreduktion ist eine Problemlösestrategie, bei der ein Problem auf ein
strukturgleiches Problem (in "verkleinerter" Form) zurückgeführt wird.
Eine rekursive Funktionsdefinition ist eine Funktionsdefinition, bei der die Funktion selbst im
Funktionsterm benutzt wird.
def umdrehen(wort):
if len(wort) == 0:
return wort
else:
return umdrehen(wort[1:]) + wort[0]
30
Ausführung rekursiver Funktionsdef.
umdrehen('LEBEN') ->
(umdrehen('EBEN') + 'L') ->
((umdrehen('BEN') + 'E') + 'L') ->
(((umdrehen('EN') + 'B') + 'E') + 'L') ->
((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') ->
(((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') ->
((((('' + 'N') + 'E') + 'B') + 'E') + 'L') ->
(((('N' + 'E') + 'B') + 'E') + 'L') ->
((('NE' + 'B') + 'E') + 'L') ->
(('NEB' + 'E') + 'L') ->
('NEBE' + 'L') ->
'NEBEL'
Bei der Auswertung von Funktionsaufrufen kommt es bei rekursiven Funktionsdefinitionen zur
wiederholten Ausführung strukturgleicher Reduktionsschritte. Rekursion kann somit als Ersatz
für die Kontrollstruktur "Wiederholung" benutzt werden.
Damit dieses Wiederholungskonzept terminiert, muss nach endlichen vielen
Reduktionsschritten eine Situation erreicht werden, bei der die Lösung zum Problem direkt
angegeben werden kann. Man versucht daher, Reduktionsschritte so zu konzipieren, dass sie
das Problem in einer Weise "verkleinern", die nur endlich viele Verkleinerungsschritte zulässt.
Rekursion ist eine mächtige und gerne genutzte Strategie beim Problemlösen. Wenn der
Problemkontext es zulässt, dann kann man das Problemlöseverfahren sehr einfach und
kompakt über eine Problemreduktion beschreiben. Die wiederholte Ausführung der
Reduktionsschritte - und damit die Erzeugung der Lösung - überlässt man dem Ausführsystem.
31
Modularisierung
Fazit:
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, die man mit Hilfe von Funktionskomposition flexibel zusammensetzen
kann. Wenn sie - wie in der funktionalen Programmierung üblich - frei von Seiteneffekten
konzipiert werden, dann können die Bausteine unabhängig genutzt werden.
Aufgaben
32
Aufgabe:
Wir betrachten die Berechnung der Summe natürlicher Zahlen.
ALGORITHMUS Summenberechnung:
{vorher: n aus 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}
iterative Lösung
Regeln Summenberechnung:
summe(0) ->
0
summe(5) ->
5 + summe(4)
rekursive Lösung
Entwickle und teste eine hierzu passende Funktionsdefinition.
Aufgaben
33
Aufgabe:
Ein Kapital von 1000 Euro wird jährlich mit 5% verzinst. Die Funktion kapital(n) beschreibe den
Kapitalwert nach n Jahren.
Die folgenden Problemreduktionsschritte sollen einem funktionalen Programm zu Grunde
liegen.
Verallgeinere diese Reduktionsschritte zu einem Programm und teste es mit mehreren
Funktionsaufrufen.
kapital(0) ->
1000
kapital(5) ->
kapital(4) + 0.05 * kapital(4)
Aufgaben
34
Aufgabe:
Ein Patient nimmt jeden Morgen 5 mg eines Medikaments ein. Im Laufe des Tages werden von
dem gesamten, im Körper befindlichen Medikament 40% abgebaut. Die Funktion
medikamentenmenge(n) beschreibe die Menge des Medikaments (in mg), die sich am n-ten
Tag morgens nach Einnahme des Medikaments im Körper befindet.
Ergänze die folgenden Problemreduktionsschritte und verallgemeinere sie zu einem
funktionalen Programm.
medikamentenmenge(0) ->
...
medikamentenmenge(5) ->
...
35
Aufgaben
Aufgabe:
Was leistet die folgendermaßen definierte Funktion?
Teste diese Funktion mit verschiedenen Aufrufen und erkläre, wie die Ergebnisse zustande
kommen.
def anzahl(element, liste):
if len(liste) == 0:
return 0
else:
if liste[0] == element:
return (1 + anzahl(element, liste[1:]))
else:
return anzahl(element, liste[1:])
36
Aufgaben
Aufgabe:
Das Problem besteht darin, ein Element aus einer Liste zu entfernen. Kommt das Element
mehrfach vor, so soll nur das erste / alle vorkommende Element entfernt werden. Die
folgenden Problemreduktionsschritte sollen den funktionalen Programmen zu Grunde liegen.
entferneErstes('b', []) ->
[]
entferneErstes('b', ['b', 'c', 'b']) ->
['c', 'b']
entferneErstes('b', ['a', 'd', 'b', 'c', 'b']) ->
['a'] + entferneErstes('b', ['d', 'b', 'c', 'b'])
entferneAlle('b', []) ->
entferneAlle('b', ['b', 'c', 'b']) ->
entferneAlle('b', ['a', 'd', 'b', 'c', 'b']) ->
37
Aufgaben
Aufgabe:
Das Problem besteht darin, jedes Element aus einer Zahleniste um einen bestimmten Wert zu
erhöhen.
Ergänze die folgenden Problemreduktionsschritte und verallgemeinere sie zu einem
funktionalen Programm.
addiere(3, []) ->
addiere(3, [4, 6, 6, 2, 0, 3]) ->
38
Aufgaben
Aufgabe:
Das Problem besteht darin, aus einer Zahlenliste alle Elemente herauszufiltern, die kleiner als
ein bestimmter Wert sind.
Erstelle erst einmal geeignete Problemreduktionsschritte. Verallgemeinere sie anschließend zu
einem funktionalen Programm.
39
Teil 3
Funktionen als Datenobjekte
40
Datensätze sortieren
Im Sportverein werden die Daten der Mitglieder mit einem Programm verwaltet. Für jedes
Mitglied liegt ein Datensatz vor, der z.B. Vorname, Nachname, Geburtsdatum etc. enthält.
Programme zur Verwaltung solcher Daten bieten in der Regel eine Sortierfunktion an. Mit Hilfe
der Sortierfunktion können die Daten für bestimmte Zwecke umsortiert werden, z.B. nach
Name und Vorname oder nach dem Geburtsdatum.
41
Datensätze sortieren
Es gibt eine Reihe von Sortierverfahren zur Realisierung der Sortierfunktion. Wir betrachten im
Folgenden das Sortieren durch Einfügen.
def einfuegen(e, L):
if len(L) == 0:
return [e]
else:
if e < L[0]:
return [e] + L
else:
return [L[0]] + einfuegen(e, L[1:])
def sortieren(L):
if len(L) == 0:
return []
else:
return einfuegen(L[0], sortieren(L[1:]))
Sortieren durch Einfügen
42
Datensätze sortieren
def einfuegen(e, L):
Alphabetische Sortierung nach
if len(L) == 0:
return [e]
dem Nachnamen
else:
if e[1] < L[0][1]:
personendaten = [
return [e] + L
('Britta', 'Koch', (23,5,1995)),
else:
('Paul', 'Schwarz', (11,11,2003)),
return [L[0]] + einfuegen(e, L[1:])
('Silke', 'Schmidt', (13,7,1990)),
('Jennifer', 'Abel', (15,12,1993)),
def sortieren(L):
('Adriana', 'Müller', (21,1,2001)),
if len(L) == 0:
('Tobias', 'Klein', (1,3,1997)),
return []
('Philipp', 'Bach', (21,4,1997)),
else:
('Oliver', 'Schmitt', (14,4,1994)),
return einfuegen(L[0], sortieren(L[1:]))
('Simone', 'Schuster', (20,12,2000)),
('Pia', 'Schwarz', (11,11,2003)),
def einfuegen(e, L):
('Andreas', 'Beyer', (16,3,1988)),
if len(L) == 0:
('Alexander', 'Heinrich', (17,3,1999)),
return [e]
('Maximilian', 'Meyer', (21,6,1986))]
else:
if e[1] < L[0][1] or (e[1] == L[0][1] and e[0] < L[0][0]):
return [e] + L
else:
return [L[0]] + einfuegen(e, L[1:])
Alphabetische Sortierung nach
def sortieren(L):
if len(L) == 0:
return []
else:
return einfuegen(L[0], sortieren(L[1:]))
dem Nach- und Vornamen
Sortierung nach dem
Geburtsdatum?
43
Flexible Vergleiche
Die Black-Box-Modellierungen zeigen Situationen, in denen eine Vergleichsoperation an die
Funktion übergeben wird. In der oberen Abbildung wird als Vergleichsoperation der Vergleich
der Nachnamen übergeben, in der unteren Abbildung ein Vergleich der Geburtsdaten.
einfuegen
('Pia', 'Schwarz', (11,11,2003))
e
[('Andreas', 'Beyer', (16,3,1988)), ...]
L
kleinerNachname
vergleich
[
('Andreas', 'Beyer', ...),
...,
('Pia', 'Schwarz', ...),
...
]
einfuegen
('Pia', 'Schwarz', (11,11,2003))
e
[('Andreas', 'Beyer', (16,3,1988)), ...]
L
juenger
vergleich
[
('Pia', 'Schwarz', (11,11,2003)),
('Andreas', 'Beyer', (16,3,1988)),
...,
]
44
Ein Parameter für Vergleichsfunktionen
Funktionale Programmierung ermöglicht solche Situationen, in denen einer Funktion Daten
übergeben werden können, die selbst eine Funktion darstellen.
einfuegen
('Pia', 'Schwarz', (11,11,2003))
e
[('Andreas', 'Beyer', (16,3,1988)), ...]
L
kleinerNachname
vergleich
[
('Andreas', 'Beyer', ...),
...,
('Pia', 'Schwarz', ...),
...
]
def einfuegen(e, L, vergleich):
if len(L) == 0:
return [e]
else:
if vergleich(e, L[0]) == True:
return [e] + L
else:
return [L[0]] + einfuegen(e, L[1:], vergleich)
def sortieren(L, vergleich):
if len(L) == 0:
return []
else:
return einfuegen(L[0], sortieren(L[1:], vergleich), vergleich)
45
Funktionsaufrufe
def einfuegen(e, L, vergleich):
if len(L) == 0:
return [e]
else:
if vergleich(e, L[0]) == True:
# Test
return [e] + L
else:
personendaten
= vergleich)
[
return [L[0]] + einfuegen(e,
L[1:],
('Jennifer', 'Abel', (15,12,1993)),
('Philipp', 'Bach', (21,4,1997)),
def sortieren(L, vergleich):
('Andreas', 'Beyer', (16,3,1988)),
if len(L) == 0:
('Alexander', 'Heinrich', (17,3,1999)),
return []
('Tobias', 'Klein', (1,3,1997)),
else:
('Britta', 'Koch',
(23,5,1995)),
return einfuegen(L[0], sortieren(L[1:],
vergleich),
vergleich)
('Maximilian', 'Meyer', (21,6,1986)),
('Adriana', 'Müller', (21,1,2001)),
('Silke', 'Schmidt', (13,7,1990)),
Aufgabe:
('Oliver', 'Schmitt', (14,4,1994)),
('Simone', 'Schuster', (20,12,2000)),
Teste selbst die
('Pia', 'Schwarz', (11,11,2003)),
verallgemeinerte
('Paul', 'Schwarz', (11,11,2003))]
Sortierfunktion.
# Situation 1
def kleinerAlphabetischNachname(person1, person2):
if person1[1] < person2[1]:
return True
else:
return False
print(sortieren(personendaten, kleinerAlphabetischNachname))
46
Aufgabe
# Test
personendaten = [...]
# Situation 2
def geburtsdatum(person):
…
def tag(datum):
…
def monat(datum):
…
def jahr(datum):
return …
def kleinerDatum(datum1, datum2):
…
def juenger(person1, person2):
…
print(sortieren(personendaten, juenger))
Aufgabe:
Die Personendaten sollen nach dem Alter der
Personen – von jung nach alt – sortiert werden.
Entwickle passende Funktionen. Du kannst dich
an dem Vorschlag orientieren.
47
Funktionen höherer Ordnung
Eine Funktion höherer Ordnung ist eine Funktion, die Funktionen als Übergabedaten erhält
oder eine Funktion als Rückgabedatum zurückliefert.
def rechnen(f, a, b):
return f(a, b)
def add(a, b):
return a + b
def sub(a, b):
return a - b
>>> rechnen(add, 3, 6)
9
>>> rechnen(sub, 5, 2)
3
48
Aufgaben
Aufgabe:
(a) Kann man die Funktion rechnen auch zur Ausführung von Vergleichsoperationen
benutzen? Führe hierzu geeignete Tests durch.
(b) Die Funktion anwenden ist folgendermaßen definiert. Teste die Funktion anwenden mit
verschiedenen konkreten Funktionen.
def anwenden(f, a):
return f(a)
49
Funktion auf Listenelemente anwenden
# Berechnungsmodell 1
def beitraegeBerechnen1(personenListe):
if personenListe == []:
return []
else:
(vorname, name, geburtsdatum) = personenListe[0]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter < 10:
beitrag = 20
elif alter < 20:
beitrag = 40
else:
beitrag = 60
personNeu = (vorname, name, geburtsdatum, beitrag)
return [personNeu] + beitraegeBerechnen2(personenListe[1:])
# Anwendung
personendaten = [('Jennifer', 'Abel', (15,12,1993)), ...]
print(beitraegeBerechnen1(personendaten))
Im Sportverein steht die Berechnung des jährlichen Mitgliedsbeitrags an. Zwei
Beitragberechnungsmodelle sollen durchgespielt werden. Im ersten Modell ist der Beitrag nur
grob gestaffelt: Kinder, Jugendliche und Erwachsene zahlen jeweils einen festen Betrag. Im
zweiten Modell ist der Beitrag direkt an das Alter (am Ende des Kalenderjahres) gekoppelt.
50
Funktion auf Listenelemente anwenden
# Berechnungsmodell 2
def beitraegeBerechnen2(personenListe):
if personenListe == []:
return []
else:
(vorname, name, geburtsdatum) = personenListe[0]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
beitrag = 3 * alter
personNeu = (vorname, name, geburtsdatum, beitrag)
return [personNeu] + beitraegeBerechnen1(personenListe[1:])
# Anwendung
personendaten = [('Jennifer', 'Abel', (15,12,1993)), ...]
print(beitraegeBerechnen2(personendaten))
Aufgabe
(a) Teste erst einmal die beiden Funktionen. Entwickle selbst ein weiteres Berechnungsmodell
und die passende Berechnungsfunktion.
(b) Vergleiche die Funktionsdefinitionen. Welche Gemeinsamkeiten fallen auf?
(c) Siehst du eine Möglichkeit, eine Funktion höherer Ordnung zu benutzen, um die
gemeinsame Berechnungsstruktur zu erfassen?
51
Der map-Operator
Verarbeitungssituation: Jedes Element einer Liste wird mit der gleichen Vorschrift verarbeitet
und durch das verarbeitete Element ersetzt.
Wir benutzen den sogenannen map-Operator zur Erfassung dieser Verarbeitungssituation.
Beispiele zur Anwendung des map-Operators:
52
Der map-Operator
def myMap(liste, f):
if liste == []:
return []
else:
return [f(liste[0])] + myMap(liste[1:], f)
personendaten = [('Jennifer', 'Abel', (15,12,1993)),...]
def beitragBerechnen1(person):
(vorname, name, geburtsdatum) = person
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter < 10:
beitrag = 20
elif alter < 20:
beitrag = 40
else:
beitrag = 60
personNeu = (vorname, name, geburtsdatum, beitrag)
return personNeu
print(myMap(personendaten, beitragBerechnen1))
def myMap(liste, f):
return [f(x) for x in liste]
53
Aufgaben
Aufgabe:
Benutze den map-Operator, um für eine Funktion eine Wertetabelle zu erstellen.
54
Listenelemente herausfiltern
def erwachsenHerausfiltern(liste):
if liste == []:
return []
else:
person = liste[0]
geburtsdatum = person[2]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter >= 18:
return [liste[0]] + erwachsenHerausfiltern(liste[1:])
else:
return erwachsenHerausfiltern(liste[1:])
# Test
personendaten = [('Jennifer', 'Abel', (15,12,1993)), ...]
# Anfrage 1
print(erwachsenHerausfiltern(personendaten))
Die Liste der Datensätze zur Verwaltung der Mitglieder des Sportvereins soll nach
verschiedenen Kriterien durchsucht werden: Wer gilt als erwachsen (d.h. ist am Ende des
Jahres mindestens 18 Jahre alt)? Wer hat im Februar Geburtstag? Wer hat einen Nachnamen,
der mit S beginnt? Es sollen alle Datensätze, die das jeweilige Suchkriterium erfüllen, ermittelt
werden.
55
Listenelemente herausfiltern
def erwachsenHerausfiltern(liste):
if liste == []:
return []
else:
person = liste[0]
geburtsdatum = person[2]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter >= 18:
return [liste[0]] + erwachsenHerausfiltern(liste[1:])
else:
return erwachsenHerausfiltern(liste[1:])
# Test
personendaten = [('Jennifer', 'Abel', (15,12,1993)), ...]
# Anfrage 1
print(erwachsenHerausfiltern(personendaten))
Aufgabe
(a) Entwickle entsprechende Funktionsdefinitionen, um die Datensätze zu den beiden anderen
oben genannten Kriterien herauszufiltern.
(b) Beschreibe das Schema, das all diesen Filteroperationen zu Grunde liegt.
(c) Siehst du eine Möglichkeit, eine Funktion höherer Ordnung zu benutzen, um das
gemeinsame Berechnungsschema zu erfassen?
56
Der filter-Operator
Verarbeitungssituation: Elemente einer Liste, die eine bestimmte Bedingung erfüllen, werden in
einer neuen Liste zusammengestellt.
Wir benutzen den sogenannen filter-Operator zur Erfassung dieser Verarbeitungssituation.
Beispiele zur Anwendung des filter-Operators:
57
Der filter-Operator
def myFilter(liste, f):
def myFilter(liste, f):
if liste == []:
return [x for x in liste if f(x)]
return []
else:
if f(liste[0]):
return [liste[0]] + myFilter(liste[1:], f)
else:
return myFilter(liste[1:], f)
personendaten = [('Jennifer', 'Abel', (15,12,1993)),...]
def erwachsen(person):
geburtsdatum = person[2]
aktuellesJahr = 2010
alter = aktuellesJahr - geburtsdatum[2]
if alter >= 18:
return True
else:
return False
# Anfrage 1
print(myFilter(personendaten, erwachsen))
58
Aufgaben
Aufgabe:
Benutze den filter-Operator, um alle positiven / negativen Zahlen aus einer Zahlenliste
herauszufiltern.
Aufgabe:
Benutze den filter-Operator, um Quicksort geschickt zu implementieren.
59
Teil 4
Deklarative Programmierung
60
Dialog über Datenverarbeitung
I: Hallo! Ich vertrete hier die imperative Programmierung.
F: Hallo, und ich vertrete die funktionale Programmierung.
I: Neulich hast du behauptet, dass man Programme ohne Variablen und
Zuweisungen schreiben kann und dass man keine Schleifen benötigt.
F: Das stimmt - zumindest so ungefähr!
I: Ich kann mir das immer noch nicht so recht vorstellen. Wie soll das z.B. bei
einem ganz einfachen Algorithmus wie dem folgenden funktionieren?
F: Umdenken ist die Devise! Der Algorithmus benutzt Befehle, um die Daten zu
verarbeiten. Die Daten werden dabei mit Hilfe von Variablen verwaltet. Ich
mache das ganz anders. Ich konzipiere eine Funktion, die das Gewünschte
leistet, und definiere sie mit Hilfe von Regeln.
I: Sehe ich das richtig, dass da auch Variablen vorkommen?
F: Ja, aber nur als Parameter, um Daten an die Funktion zu übergeben. Der
entscheidende Unterschied ist, dass sie ihre Werte nicht verändern.
I: Das ist ja alles schön und gut. Aber, wozu soll das Ganze gut sein? Ich
komme mit meiner Methode doch auch zum Ziel.
F: Stimmt, wir kommen beide zum Ziel, aber mit ganz unterschiedlichen
Methoden. Du benutzt Algorithmen, die letztlich aus Anweisungen bzw.
Befehlen bestehen. Diese Befehle richten sich an eine tatsächliche oder
gedachte Maschine. Bei der Entwicklung von Algorithmen musst du also denken
wie eine Maschine.
I: Das habe ich zwar noch nie so empfunden, aber eigentlich hast du recht. Und
wie denkst du?
ALGORITHMUS Summenberechnung:
{vorher: n aus 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}
FUNKTION summe:
{Fall 1:}
n == 0: summe(n)->0
{Fall 2:}
n > 0: summe(n)->n+summe(n-1)
61
Dialog über Datenverarbeitung
F: Ich entwickle Beschreibungen anstatt Befehle zu erteilen. Ich beschreibe das
gewünschte Berechnungsverhalten mit einer Funktion, die den Ausgangsdaten
die Zieldaten zuordnet. Anschließend lege ich mit Hilfe von Regeln die
Zuordnung präzise fest.
I: Das erinnert mich irgendwie an Funktionsdefinitionen in der Mathematik.
F: Ja, da macht man das ganz genauso.
I: Und warum sollen wir beim Programmieren vorgehen wie Mathematiker? Hat
das irgendwelche Vorteile?
F: Beim Programmieren macht man leicht Fehler. Gib zu, dass du auch schon
mal die Anweisung "erhöhe i um 1" vergessen hast. Wenn man funktional
programmiert, kann man sich leichter von der Korrektheit der Programme
überzeugen. Ich mache mir die Korrektheit von Regeln immer anhand typischer
Beispiele klar:
I: Ok, das sehe ich ein! Aber, wenn ich mich so umschaue, stelle ich fest, dass
fast alle imperativ programmieren wie ich. So richtig durchgesetzt hast du dich
noch nicht, oder?
F: Viele kennen mich halt nicht - das ist irgendwie schade. Die, die mich
kennen, benutzen meinen deklarativen Ansatz beim Entwickeln von Prototypen.
Es geht hier darum, schnell ein System zu entwickeln, das korrekt arbeitet bzw.
das gewünschte Verhalten zeigt. Es kommt nicht darauf an, das
Laufzeitverhalten zu optimieren oder Benutzerfreundlichkeit zu gewährleisten.
I: Kannst du das an einem Beispiel klarmachen?
F: Gerne, aber erst im nächsten Abschnitt.
FUNKTION summe:
{Fall 1:}
n == 0: summe(n)->0
{Fall 2:}
n > 0: summe(n)->n+summe(n-1)
62
Beschreiben statt Befehlen
Imperative Programmierung besteht darin, eine (mehr oder weniger abstrakte) Maschine mit
Hilfe von Anweisungen zu steuern.
Dabei wird beschrieben, wie die Ergebnisse berechnet werden sollen. Zentrale Bausteine
imperativer Programme sind Wertzuweisungen, die i.a. den momentanen Variablenzustand
(Speicherzustand) verändern. Imperative Programmierung ist wegen der Möglichkeit,
Seiteneffekte zu produzieren, recht fehleranfällig.
Deklarative Programmierung besteht darin, den Problemkontext (die Miniwelt) mit gegebenen
Mitteln zu beschreiben. Funktionale Programmierung - als eine Form der deklarativen
Programmierung - benutzt hierzu Funktionen.
Bei der deklarativen Programmierung wird somit beschrieben, was in der Modellwelt gelten
soll.
Die funktionale Programmierung arbeitet dabei ohne Speichervariablen. Variablen kommen hier
nur als Funktionsvariablen zur Übergabe von Funktionsargumenten vor. Seiteneffekte sind
demnach in der funktionalen Programmierung nicht möglich. Das Verhalten einer Funktion wird
vollständig durch die Funktionsdeklarationen festgelegt.
Funktionale Programmierung erfolgt auf einem höheren Abstraktionsniveau. Es ergeben sich
dadurch Programme, die kürzer, leichter zu durchschauen und weniger fehleranfälliger sind.
Funktionale Programmierung eignet sich zum „Prototyping“.
63
Prototyping
Bei der Entwicklung neuer Produkte wird zunächst oft ein Prototyp erstellt. Dabei handelt es
sich um ein funktionsfähiges Modell, das dem geplanten Produkt in vielen Bereichen
entspricht, meist aber auch eine Reihe von Vereinfachungen aufweist.
Prototypen werden - insbesondere bei komplexen
technischen Systemen - zunächst einmal erstellt, um
Erfahrungen mit dem neuen System und seiner
Entwicklung zu sammeln.
Prototypen kommen entsprechend auch bei der
Entwicklung komplexer Software zum Einsatz. Wir
skizzieren im Folgenden, wie ein Prototyp zu einem
RSA-Kryptosystem mit Hilfe der funktionalen
Programmierung erstellt werden kann.
64
Prototyp zu einem Kryptosystem
Beim RSA-Verfahren werden natürliche Zahlen mit Hilfe eines (öffentlichen bzw. privaten)
Schlüssels in neue Zahlen umwandelt.
from kryptosystem_rsa import *
# Testprogramm
n = 116063
e = 4097
d = 51137
oeffentlicherSchluessel = (e, n)
privaterSchluessel = (d, n)
s0 = 'Köln'
print(s0)
s1 = verschluesseln(s0, oeffentlicherSchluessel, codierenZeichenketteZahlenliste)
print(s1)
s2 = entschluesseln(s1, privaterSchluessel, decodierenZahlenlisteZeichenkette)
print(s2)
>>>
Köln
[99070, 100824, 50230]
Köln
Die Funktionen zur Realisierung des Prototyps findet man auf den Seiten von www.infschule.de (1.22.4.2).
65
Literaturhinweise
[Becker 99] K. Becker: Funktionale Programmierung. Materialien zum Lehrplan Informatik.
LMZ 1999. (http://informatikag.bildung-rp.de/html/funktprog.html)
[Becker 00] K. Becker: Problemlösen mit dem Computeralgebrasystem Derive - informatisch
betrachtet. (http://informatikag.bildung-rp.de/html/derive.html)
[Becker 04] K. Becker: Funktionale Programmierung mit Caml. (http://informatik.bildungrp.de/fileadmin/user_upload/informatik.bildung-rp.de/Weiterbildung/pps/WBFunktionaleProgrammierungCaml.pps)
[Lorenz 12] G. Funktionale Modellierung und Rekursion. Oldenbourg Verlag 2012.
…
Herunterladen