Transcript Powerpoint-Präsentation
Slide 1
Funktionale Programmierung
Klaus Becker
2007
Slide 2
2
Programmieren mit Funktionen
Mit Funktionen kann man programmieren. Die
Programme bestehen aus Funktionsdeklarationen.
Funktionsdeklarationen werden mit Hilfe von
Funktionskomposition, Fallunterscheidungen und
Rekursion aufgebaut. ...
Zielsetzung: Einblick in die funktionale Programmierung gewinnen
Grundideen verstehen
Grundkonzepte kennen lernen
Relevanz anhand von Miniprojekten erkennen
Slide 3
3
Teil 1
Von der Registermaschine
zur funktionalen Abstraktion
Slide 4
Registermaschine
4
Eine Registermaschine bearbeitet beliebig eingebbare Daten nach einem
fest vorgegebenen Programm.
Daten
1:
2:
3:
4:
5:
5
3
0
0
0
..
> 1
2
3
4
5
6
JMP
INC
DEC
TST
JMP
HLT
4
1
2
2
2
Programm
> 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.
Slide 5
Aufgabe
5
Was leistet das unten abgebildete Registermaschinenprogramm?
Messen Sie die Zeit, die Sie brauchen, um das herauszufinden.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
Slide 6
6
Aufgabe
Was leistet das unten abgebildete (Python-) Programm?
Messen Sie die Zeit, die Sie brauchen, um das herauszufinden.
while (R2 <> 0) and (R3 <> 0):
R1 = R1 + 1
R2 = R2 - 1
R3 = R3 - 1
while R2 <> 0:
R2 = R2 - 1
while R3 <> 0:
R3 = R3 -1
Slide 7
7
Spaghetti-Code mit Sprunganweisungen
Durch die vielen Sprunganweisungen verliert man leicht
den Überblick über die Ablauflogik.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
Wer strukturiert programmiert, verzichtet auf solche
Sprunganweisungen, auch wenn die
Programmiersprache sie zur Verfügung stellt.
Viele Programmiersprachen stellen keine
Sprunganweisungen zur Verfügung. Dann können solch
schwer zu durchschauenden Programme gar nicht erst
geschrieben werden.
"Spaghetti-Code"
Slide 8
8
Ablaufkontrolle mit Kontrollanweisungen
An Stelle von Sprunganweisungen benutzt man Kontrollstrukturen, um die
Ablauflogik strukturiert zu beschreiben.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
while (R2 <> 0) and (R3 <> 0):
R1 = R1 + 1
R2 = R2 - 1
R3 = R3 - 1
while R2 <> 0:
R2 = R2 - 1
while R3 <> 0:
R3 = R3 -1
Ablaufbeschreibung mit Kontrollstrukturen
Ablaufbeschreibung mit Sprunganweisungen
Slide 9
9
Aufgabe
Vergleichen Sie die beiden Programme. Welche Version ist besser?
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, 7)
>>> m
5
def minimum(a, b):
m = 0
while (a <> 0) and (b <> 0):
m = m + 1
a = a - 1
b = b - 1
return m
>>> minimum(5, 7)
5
Slide 10
Seiteneffekte
10
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
>>>
>>>
>>>
8
>>>
>>>
>>>
5
m = 3
minimum(5, 7)
m
m = 0
minimum(5, 7)
m
Unterprogramm mit Seiteneffekt
def minimum(a, b):
m = 0
while (a <> 0) and (b <> 0):
m = m + 1
a = a - 1
b = b - 1
return m
>>> minimum(5, 7)
5
Slide 11
11
Programme ohne Seiteneffekte
Eine Funktion soll normalerweise aus den Argumenten einen Wert
berechnen und nichts anderes nebenbei tun. Von diesem Prinzip weichen
Programmierer jedoch immer wieder ab, etwa indem sie in der Funktion
globale Variablen verändern. Wer strukturiert programmiert, verzichtet auf
Seiteneffekte.
Es gibt Programmiersprachen, die keine Seiteneffekte zulassen. Die Idee
dabei ist, auf Wertzuweisungen zu verzichten, so dass keine
unbeabsichtigten Seiteneffekte möglich sind.
def minimum(a, b):
if a == 0:
return a
else:
if b == 0:
return b
else:
return 1 + minimum(a-1, b-1)
Unterprogramm ohne Wertzuweisung
Slide 12
12
Zielsetzung
Ziel ist es, die Grundideen funktionaler Programmierung anhand von
Beispielen problemorientiert zu erarbeiten. Insbesondere soll dabei
herausgearbeitet werden, dass Programmierung ohne Wertzuweisungen
möglich ist.
Slide 13
13
Teil 2
Konzepte der funktionalen Programmierung
Slide 14
Fallstudie: Chiffrieren
14
Ziel ist es, Chiffrierverfahren (die auf modularem Rechnen basieren), mit
Hilfe funktionaler Programme zu beschreiben.
PYLZFOWBNQCYBUVNCBLGYC
HYAYBYCGMWBLCZNYHNTCZY
LN
VDOYH
FDHVDU
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
Quelltext:
SALVECAESAR
Schlüssel: D
Geheimtext:
VDOYHFDHVDU
Slide 15
15
Additives Chiffrierverfahren
Codierung:
Code: A → 1
Blocklänge: 2
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
Decodierung:
Code: A → 1
Blocklänge: 2
AA → 0101
AB → 0102
...
ZZ → 2626
0119#2005#1809#24
z → (z + d) % m
0119#2005#1809#24
Bed.: z < m
m > maxCode
z → (z + e) % m
Bed.:
(d + e) % m = 0
AA → 0101
AB → 0102
...
ZZ → 2626
AS#TE#RI#X
2221#1107#1010#2126
2221#1107#1010#2126
0119#2005#1809#24
0119#2005#1809#24
AS#TE#RI#X
Slide 16
16
Additives Chiffrierverfahren
Wir betrachten zunächst das Verschlüsseln und Entschlüsseln von
Zahlenfolgen mit Schlüsseln, die aus zwei Komponenten bestehen. Da Verund Entschlüsseln gleich funktionieren, reicht es, nur das Verschlüsseln zu
betrachten.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
z → (z + d) % m
0119#2005#1809#24
Bed.: m ist größer als
die maximale Codezahl
2221#1107#1010#2126
z → (z + e) % m
2221#1107#1010#2126
Bed.:
d+e=m
0119#2005#1809#24
Slide 17
Funktionale Modellierung
17
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Eingaben:
zahl: die zu verschlüsselnde Zahl
schluessel: Zahlenpaar bestehend aus der zu addierenden Konstante und dem Divisionsmodul
Ausgabe:
die berechnete verschlüsselte Zahl
Slide 18
Funktionale Modellierung
18
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Eingaben:
zahlenListe: Liste mit den zu verschlüsselnden Zahlen
schluessel: Zahlenpaar bestehend aus der zu addierenden Konstante und dem Divisionsmodul
Ausgabe:
Liste mit den berechneten verschlüsselten Zahlen
Slide 19
Funktionales Programm
19
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Implementierung:
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
Slide 20
Funktionales Programm
20
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Implementierung:
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 21
Datenstruktur "Tupel"
21
Tupel sind unveränderbare Sequenzen. Man verwendet Tupel, wenn man
ein Objekt repräsentieren möchte, das aus mehreren Komponenten
besteht.
Spezifikation:
Tupel
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Implementierung:
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
Zugriff auf die Komponenten
Slide 22
Datenstruktur "Liste"
22
Listen sind veränderbare Sequenzen. Sie können (in Python) Objekte
beliebigen Typs enthalten. Mit Listen kann man komplexere Strukturen
modellieren.
Spezifikation:
Liste
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
Implementierung:
zahlenListe
schluessel
Liste
[2221, 1107, 1010, 2126]
Listenoperationen
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 23
23
Listenoperationen
Das Python-Protokoll zeigt die Listenoperationen, die im Folgenden benötigt
werden. Weitere Listenoperationen findet man in der Dokumentation.
>>> zahlenListe = []
>>> zahlenListe
[]
>>> len(zahlenListe)
0
>>> zahlenListe = [119, 2005, 1809, 24]
>>> zahlenListe
[119, 2005, 1809, 24]
>>> zahlenListe[0]
119
>>> zahlenListe[1:]
[2005, 1809, 24]
>>> [zahlenListe[0]] + zahlenListe[1:]
[119, 2005, 1809, 24]
>>>
...
leere Liste
Länge einer Liste
Zugriff auf Listenelemente
Zugriff auf eine Restliste
Konkatenation von Listen
Slide 24
24
Aufgabe
Testen Sie die im funktionalen Programm vorkommenden
Listenoperationen. Variieren Sie dabei auch die vorkommenden
Parameterwerte.
>>> zahlenListe = []
>>> zahlenListe
[]
>>> len(zahlenListe)
0
>>> zahlenListe = [119, 2005, 1809, 24]
>>> zahlenListe
[119, 2005, 1809, 24]
>>> zahlenListe[0]
119
>>> zahlenListe[1:]
[2005, 1809, 24]
>>> [zahlenListe[0]] + zahlenListe[1:]
[119, 2005, 1809, 24]
>>>
...
Slide 25
Rekursive Problemreduktion
25
Rekursive Problemreduktion: Reduziere des Problems auf ein
entsprechendes, aber „verkleinertes“ Problem.
Fall 1: Bearbeite eine leere Liste
verschluesseln([], (2102, 3000))
→ []
Rekursionsanfang:
Löse das Problem direkt
[]
Fall 2: Bearbeite eine nicht-leere Liste
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
[2221, 1107, 1010, 2126]
→ [verschluesselnZahl(119, (2102, 3000))] +
[2221]
verschluesseln([2005, 1809, 24], (2102, 3000))
[1107, 1010, 2126]
Rekursionsschritt:
Löse ein entsprechendes
Problem
Slide 26
26
Aufgabe
Fügen Sie Ausgabeanweisungen ein und verfolgen Sie so die Auswertung
der einzelnen Funktionsaufrufe.
def verschluesseln(zahlenListe, schluessel):
print zahlenListe, schluessel
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 27
27
Kontrollstruktur "Rekursion"
Rekursion wird in der funktionalen Programmierung als Kontrollstruktur
benutzt, um wiederkehrende Berechnungen durchzuführen.
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
→ [verschluesselnZahl(119, (2102, 3000))] +
verschluesseln([2005, 1809, 24], (2102, 3000))
→ [2221] +
verschluesseln([2005, 1809, 24], (2102, 3000))
→ [2221] +
[verschluesselnZahl(2005, (2102, 3000))] +
verschluesseln([1809, 24], (2102, 3000))
→ [2221] +
[1107] +
verschluesseln([1809, 24], (2102, 3000))
...
Slide 28
28
Kontrollstruktur "Rekursion"
→ [2221] +
[1107] +
[verschluesselnZahl(1809, (2102, 3000))] +
verschluesseln([24], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
verschluesseln([24], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[verschluesselnZahl(24, (2102, 3000))] +
verschluesseln([], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[2126] +
verschluesseln([], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[2126] +
[]
→ [2221, 1107, 1010, 2126]
Slide 29
Kontrollstrukturen
29
Funktionsdeklarationen werden in der funktionalen Programmierung mit
Hilfe von
- Funktionskomposition,
- Fallunterscheidungen und
- Rekursion
aufgebaut. Diese Strukturen legen letztlich die Ablaufkontrolle fest.
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Fallunterscheidung
Rekursion
Funktionskomposition
Slide 30
Aufgabe
30
Zur Übung rekursiver Funktionsdeklarationen sollen folgende Funktionen
implementiert werden. Gehen Sie analog zur Funktion "verschluesseln" vor.
Spezifikation:
addieren
[6, 13, 0, 5]
7
zahlenListe
konstante
[13, 20, 7, 12]
Spezifikation:
nullErsetzen
[6, 0, 13, 0, 0, 5, 3]
7
zahlenListe
konstante
[6, 7, 13, 7, 7, 5, 3]
Slide 31
31
Aufgabe
In der Datei "ChiffriersystemModularesAddierenRekursiv.py" finden Sie eine
Implementierung des Chiffriersystems basierend auf modularer Addition,
die (außer zum Testen) keine Wertzuweisungen benutzt. Analysieren Sie die
Funktionsdeklarationen auch im Hinblick auf die vorkommenden
Kontrollstrukturen.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
z → (z + e) % m
Bed.:
(d + e) % m = 0
0119#2005#1809#24
2221#1107#1010#2126
2221#1107#1010#2126
0119#2005#1809#24
Slide 32
32
Aufgabe
Ändern Sie die entsprechenden Funktionsdeklarationen so ab, dass man das
multiplikative Chiffrierverfahren erhält.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (7, 30)
Entschlüsselung:
privater Schlüssel
(e, m) = (13, 30)
z → (z * d) % m
Bed.:
zz → (z * e) % m
Bed.:
(d * e) % m = 1
01#19#20#05#18#09#24
07#13#20#05#06#03#18
07#13#20#05#06#03#18
01#19#20#05#18#09#24
Slide 33
33
Codeduplizierung
Die Implementierungen zur Verschlüsselung mit modularer Addition und
modularer Multiplikation weisen auffallende Ähnlichkeiten auf.
def verschluesselnZahlAdd(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesselnAdd(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahlAdd(zahlenListe[0], schluessel)] +
verschluesselnAdd(zahlenListe[1:], schluessel)
def verschluesselnZahlMul(zahl, schluessel):
return (zahl * schluessel[0]) % schluessel[1]
def verschluesselnMul(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahlMul(zahlenListe[0], schluessel)] +
verschluesselnMul(zahlenListe[1:], schluessel)
Slide 34
Funktionen als Eingabeobjekte
34
Codeduplizierung lässt sich vermeiden, wenn man Funktionen als
Eingabeobjekte von Funktionen zulässt.
Spezifikation:
Funktion
verschluesseln
z → (z + d) % m
[119, 2005, 1809, 24]
(2102, 3000)
Liste
Tupel
verfahren
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Slide 35
35
Funktionen als Eingabeobjekte
Die Implementierung zeigt, dass man Funktionen hier wie andere Objekte
auch als Parameter übergeben kann.
def verschluesselnZahlAddieren(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesselnZahlMultiplizieren(zahl, schluessel):
return (zahl * schluessel[0]) % schluessel[1]
def verschluesselnZahl(verfahren, zahl, schluessel):
return verfahren(zahl, schluessel)
def verschluesseln(verfahren, zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(verfahren, zahlenListe[0], schluessel)] +
verschluesseln(verfahren, zahlenListe[1:], schluessel)
def verschluesselnModularesAddieren(zahlenListe, schluessel):
return verschluesseln(verschluesselnZahlAddieren, zahlenListe, schluessel)
def verschluesselnModularesMultiplizieren(zahlenListe, schluessel):
return verschluesseln(verschluesselnZahlMultip.., zahlenListe, schluessel)
Slide 36
36
Aufgabe
Testen Sie die Implementierung in der Datei
"ChiffriersystemModularesRechnenRekursiv.py".
Erweitern Sie diese Implementierung um die Möglichkeit, Verschlüsselung
auch mit modularem Potenzieren durchzuführen.
Slide 37
37
Funktionale Programmierung
Mit Funktionen kann man programmieren. Die
Programme bestehen aus Funktionsdeklarationen.
Ein Programmaufruf erfolgt mit einem
funktionalen Berechnungsausdruck.
Funktionsdeklaration
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
Berechnungsausdruck
Slide 38
38
Funktionale Programmierung
Funktionsdeklarationen werden mit Hilfe von
- Funktionskomposition,
- Fallunterscheidungen und
- Rekursion
aufgebaut.
Funktionskomposition
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
Rekursion
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Fallunterscheidung
Slide 39
39
Funktionale Programmierung
Objekte können mit Hilfe von Tupelbildung und
Listen zu neuen Einheiten zusammengefasst
werden.
Funktionen können als Eingabeobjekte für weitere
Funktionen benutzt werden.
Funktion
Tupel
def verschluesselnZahl(verfahren, zahl, schluessel):
return verfahren(zahl, schluessel)
def verschluesseln(verfahren, zahlenListe, schluessel):
if len(zahlenListe) == 0:
Liste
return []
else:
return [verschluesselnZahl(verfahren, zahlenListe[0], schluessel)] +
verschluesseln(verfahren, zahlenListe[1:], schluessel)
Slide 40
40
Miniprojekte
Funktionale Programmierung eignet sich sehr gut, um schnell ein System zu
entwickeln und zu testen. Man konzentriert sich nur auf die "Logik" des
Systems, nicht auf "schmückendes Beiwerk". Die Programme sollen dabei
kurz und gut überschaubar sein.
Im Folgenden soll diese Vorgehensweise anhand von drei Miniprojekten
aufgezeigt werden.
Slide 41
41
Miniprojekt "Geometrische Abbildungen"
Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische
Operationen durchführen kann.
Slide 42
42
Miniprojekt "Automatensimulator"
Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines
beliebigen Automaten simulieren kann.
0
0
akzeptor
1
g
u
Ok!
1
00011011
Slide 43
43
Miniprojekt "Programminterpreter"
Ziel ist es, ein System zur Ausführung einfacher imperativer Programme zu
entwickeln.
{b: 2; u: 5}
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
{b: 256; u: 0; p: 32}
Interpreter
Slide 44
44
Aufgabe
Wählen Sie eines der Miniprojekte aus und realisieren Sie das skizzierte
System. Sie können versuchen, das System völlig selbständig zu entwickeln,
oder die im Folgenden angebotenen Hilfen zu nutzen.
Slide 45
45
Teil 3
Miniprojekt: Geometrische Abbildungen
Slide 46
46
Geometrische Abbildungen
Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische
Operationen durchführen kann.
Slide 47
47
Schritt 1: Verschiebungen
Verschiebungen können mit Hilfe von Vektoren dargestellt werden. Die
Abbildung von Punkten lässt sich dann durch Addition von Vektoren
realisieren.
Vieleck:
A(0, 0), B(40, 0), C(40, 30), D(0, 30)
Verschiebung mit Vektor:
v=
10
10
verschobenes Vieleck:
A'(10, 10), B'(50, 10), C'(50, 40), D'(10, 40)
Slide 48
48
Listenrepräsentation
Wir repräsentieren Vektoren mit Hilfe von Listen. Ebenso stellen wir Punkte
und Punktfolgen mit Hilfe von Listen dar.
Vieleck:
[[0, 0], [40, 0], [40, 30], [0, 30]]
Verschiebung mit Vektor [10, 10]:
[0, 0] + [10, 10] [10, 10]
[40, 0] + [10, 10] [50, 10]
[40, 30] + [10, 10] [50, 40]
[0, 30] + [10, 10] [10, 40]
verschobenes Vieleck:
[[10, 10], [50, 10], [50, 40], [10, 40]]
Slide 49
Aufgabe
49
Entwickeln Sie ein funktionales Programm für die Addition von Vektoren.
Um das Programm flexibel benutzen zu können, sollen Vektoren beliebig
viele Komponenten haben können.
Spezifikation:
add
[60, 30, 40]
v
[10, 0, 30]
w
[70, 30, 70]
Slide 50
Aufgabe
50
Realisieren Sie jetzt die Abbildung "Verschieben" mit Hilfe der bereits
implementierten Vektoraddition.
Spezifikation:
verschiebenPunkt
[40, 30]
punkt
[10, 10]
vektor
[50, 30]
Spezifikation:
verschieben
[[20, 10], [40, 30], [70, 0]]
vieleck
[10, 10]
vektor
[[30, 20], [50, 40], [80, 10]]
Slide 51
Lösungsvorschlag
51
Spezifikation:
add
[60, 30, 40]
v
[10, 0, 30]
w
[70, 30, 70]
Implementierung:
def add(v, w):
if len(v) == 0:
if len(w) == 0:
return []
else:
return w
else:
if len(w) == 0:
return v
else:
return [v[0] + w[0]] + add(v[1:], w[1:])
Slide 52
Lösungsvorschlag
52
Spezifikation:
verschiebenPunkt
[40, 30]
punkt
[10, 10]
vektor
Spezifikation:
[50, 30]
verschieben
[[20, 10], [40, 30], [70, 0]]
vieleck
[10, 10]
vektor
[[30, 20], [50, 40], [80, 10]]
def verschiebenPunkt(punkt, vektor):
return add(punkt, vektor)
def verschieben(vieleck, vektor):
if len(vieleck) == 0:
return []
else:
return [verschiebenPunkt(vieleck[0], vektor)] +
verschieben(vieleck[1:], vektor)
Slide 53
53
Test mit Turtlegrafik
Testen Sie die einzelnen
Funktionsdeklarationen mit Hilfe
geeigneter Testfälle.
Zur visuellen Kontrolle können Sie die
geometrischen Objekte auch mit Hilfe
von Turtlegrafik darstellen. Kopieren
Sie zu diesem Zweck die Daten
"xturtle.py" (von G. Lingl aus dem
Buch "Python für Kids") in die
Standardbibliothek von Python.
Benutzen Sie die Hilfsfunktion zum
Zeichnen von Vielecken (siehe
"GeometrischeOperationen1.py").
Beachten Sie, dass diese Hilfsfunktion
Seiteneffekte in Form von Grafiken
erzeugt und damit nicht mehr rein
funktional ist.
Slide 54
54
Test mit Turtlegrafik
def zeichneVieleck(punkte):
# wird nur zum Zeichnen benutzt
if len(punkte) > 0:
penup()
setpos(punkte[0])
pendown()
streckenzug = punkte[1:] + [punkte[0]]
for punkt in streckenzug:
goto(punkt)
if __name__ == "__main__":
# Test der einzelnen Funktionen
import doctest
doctest.testmod(verbose=True)
# Visueller Test mit Turtlegrafik
from xturtle import *
reset()
vieleck1 = [[0, 0], [40, 0], [40, 30], [0, 30]]
vieleck2 = verschieben(vieleck1, [10, 10])
zeichneVieleck(vieleck1)
zeichneVieleck(vieleck2)
Slide 55
55
Schritt 2: Streckungen
Streckungen lassen sich durch
Multiplikation eines Vektors mit
einer Zahl realisieren.
Fall1: Streckzentrum im Ursprung
Streckung mit dem Faktor k:
multipliziere die Koordinaten der Punkte mit
dem Streckfaktor k
Fall 2: Streckzentrum beliebig
Verschiebe erst das Streckzentrum in den
Ursprung, führe die Steckung aus und
verschiebe wieder zurück an den
Ausgangspunkt
Beispiel:
Vieleck:
A(30, 0), B(70, 0), C(70, 30), D(30, 30)
Streckung mit dem Streckzentrum (0, 0) und
dem Streckfaktor 2:
gestrecktes Vieleck:
A'(60, 0), B'(140, 0), C'(140, 60), D'(60, 60)
Slide 56
56
Listenrepräsentation
Wir benötigen hier die Multiplikation eines Vektors mit einer Zahl.
Vieleck:
[[20, 0], [60, 0], [60, 30], [20, 30]]
Streckung mit dem Streckzentrum [0, 0] und
dem Streckfaktor 2:
2*[20, 0] [40, 0]
...
gestrecktes Vieleck:
[[40, 0], [120, 0], [120, 60], [40, 60]]
Slide 57
57
Aufgabe
Entwickeln Sie geeignete Funktionen zur Durchführung von Streckungen.
Slide 58
58
Lösungsvorschlag
def mul(k, v):
if len(v) == 0:
return []
else:
return [k * v[0]] + mul(k, v[1:])
def streckenPunktUrsprung(punkt, faktor):
return mul(faktor, punkt)
def streckenUrsprung(vieleck, faktor):
if len(vieleck) == 0:
return []
else:
return [streckenPunktUrsprung(vieleck[0], faktor)] +
streckenUrsprung(vieleck[1:], faktor)
def strecken(vieleck, zentrum, faktor):
return verschieben(
streckenUrsprung(
verschieben(vieleck, mul(-1, zentrum)),
faktor),
zentrum)
Slide 59
59
Schritt 3: Drehungen
Drehungen lassen sich durch
Multiplikation einer Matrix mit
einem Vektor realisieren.
Fall1: Drehzentrum im Ursprung
Drehung des Punktes P(x, y) um den
Winkel w:
cos(w) -sin(w)
x
*
sin(w)
cos(w)
x'
y
y'
Es gilt:
x' = cos(w)*x + (- sin(w))*y
y' = sin(w)*x + cos(w)*y
Fall 2: Drehzentrum beliebig
Verschiebe erst das Drehzentrum in den
Ursprung, führe die Drehung aus und
verschiebe wieder zurück an den
Ausgangspunkt
Slide 60
60
Listenrepräsentation
Wir repräsentieren Matrizen hier ebenfalls mit Hilfe von Listen.:
Vieleck:
[[20, 0], [60, 0], [60, 30], [20, 30]]
Matrix für eine Linksdrehung um das
Drehzentrum [0, 0] und dem Drehwinkel
90°: [[0, -1], [1, 0]]
[[0, -1], [1, 0]] * [20, 0] [0, 20]
...
gedrehtes Vieleck:
[[0, 20], [0, 60], [-30, 60], [-30, 20]]
Slide 61
61
Aufgabe
Entwickeln Sie geeignete Funktionen zur Durchführung von Drehungen. Bei
der Implementierung mit Python müssen Sie durch "import math" die
benötigten mathematischen Operationen bereitstellen. Ein Aufruf der cosFunktion lautet hier "math.cos(x)". Das Ergebnis wird im Bogenmaß
geliefert. Mit "math.radians(w)" können Sie einen Winkel im Gradmaß ins
Bogenmaß umrechen. Die umgekehrte Operation führt "math.degrees(x)"
aus. Weitere Informationen erhalten Sie in der mitgelieferten
Dokumentation.
Slide 62
62
Lösungsvorschlag
def skalprod(v, w):
if len(v) == 0:
return 0
else:
return (v[0] * w[0]) + skalprod(v[1:], w[1:])
def matrixmul(m, v):
if len(m) == 0:
return []
else:
return [skalprod(m[0], v)] + matrixmul(m[1:], v)
Slide 63
63
Lösungsvorschlag
def drehenPunktUrsprung(punkt, w):
import math
return matrixmul([
[math.cos(math.radians(w)), -math.sin(math.radians(w))],
[math.sin(math.radians(w)), math.cos(math.radians(w))]], punkt)
def drehenUrsprung(vieleck, w):
if len(vieleck) == 0:
return []
else:
return [drehenPunktUrsprung(vieleck[0], w)] +
drehenUrsprung(vieleck[1:], w)
def drehen(vieleck, zentrum, winkel):
return verschieben(
drehenUrsprung(
verschieben(vieleck, mul(-1, zentrum)),
winkel),
zentrum)
Slide 64
64
Weitere Ideen
Erweitern Sie das System um die Möglichkeit, Spiegelungen durchzuführen.
Slide 65
65
Teil 4
Miniprojekt: Automatensimulator
Slide 66
66
Miniprojekt "Automatensimulator"
Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines
beliebigen Automaten simulieren kann.
0
0
akzeptor
1
g
u
Ok!
1
00011011
Slide 67
Schritt 1: Automatenbeschreibung
67
Mit Hilfe endlicher Automaten kann man formale Sprachen erkennen. Der
dargestellte endliche Automat erkennt die Sprache der 0-1-Wörter mit
gerader Parität (gerader Anzahl von 1en). Es handelt sich um einen sog.
Akzeptor, der keine Ausgaben erzeugt.
0
0
1
g
u
1
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
Anfangszustand:
za = g
Endzustände:
zE = {g}
Überführungsfunktion:
:
:
:
:
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
Slide 68
Aufgabe
68
Der Automat lässt sich wie folgt mit
Funktionen modellieren. Implementieren Sie diese Funktionen.
0
0
1
Spezifikation:
g
anfangszustandP
'g'
Spezifikation:
endzustandP
'g'
z
True
Spezifikation:
deltaP
'g'
z
'0'
e
'g'
u
1
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
Anfangszustand:
za = g
Endzustände:
zE = {g}
Überführungsfunktion:
:
:
:
:
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
Slide 69
69
Aufgabe
Modellieren und implementieren Sie
noch einen weiteren Automaten
(ohne Ausgabe).
Slide 70
70
Aufgabe
Der unten abgebildete Automat zur Steuerung einer Ampel ist ein
Transduktor. In jedem Zustand wird bei jeder Eingabe eine Ausgabe
erzeugt. Z. B. wird im Zustand "rot" bei der Eingabe "t" (Tag) die Ausgabe
"OOo" (rot an, gelb an, grün aus) erzeugt. Beschreiben Sie diesen
Transduktor mit Hilfe von Funktionen.
Slide 71
71
Lösungsvorschlag
def anfangszustandP():
return 'g'
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
def endzustandP(z):
if (z == 'g'):
return True
elif (z == 'u'):
return False
Anfangszustand:
za = g
Endzustände:
zE = {g}
def deltaP(z, e):
if (z == 'g') and (e == '0'):
return 'g'
elif (z == 'u') and (e == '0'):
return 'u'
elif (z == 'g') and (e == '1'):
return 'u'
elif (z == 'u') and (e == '1'):
return 'g'
Überführungsfunk.: :
:
:
:
0
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
0
1
g
u
1
Slide 72
72
Lösungsvorschlag
def anfangszustandA():
return 'ge';;
def deltaA(z, e):
if (z == 'ro') and (e == 't'):
return 'rg'
elif (z == 'rg') and (e == 't'):
return 'gr'
elif (z == 'gr') and (e == 't'):
return 'ge'
elif (z == 'ge') and (e == 't'):
return 'ro'
...
def lambdaA(z, e):
if (z == 'ro') and (e == 't'):
return 'OOo'
elif (z == 'rg') and (e == 't'):
return 'ooO'
elif (z == 'gr') and (e == 't'):
return 'oOo'
...
Slide 73
73
Schritt 2: Verarbeitung v. Eingabefolgen
Es soll ein Akzeptor-System entwickelt werden, mit dem eine Folge von
Eingaben verarbeiten werden kann und rückgemeldet wird, ob diese Folge
in einen Endzustand überführt.
0
0
1
Ok!
00011011
g
u
1
Slide 74
74
Aufgabe
Erweitern Sie das funktionale Programm
um die spezifizierten Funktionen.
Tipp: siehe nächste Folie
def anfangszustandP():
return 'g'
def endzustandP(z):
if (z == 'g'):
return True
elif (z == 'u'):
return False
def deltaP(z, e):
if (z == 'g') and (e == '0'):
return 'g'
elif (z == 'u') and (e == '0'):
return 'u'
elif (z == 'g') and (e == '1'):
return 'u'
elif (z == 'u') and (e == '1'):
return 'g'
0
0
1
g
u
1
Spezifikation:
simulatorP
'g'
['0', '1', '0']
z
eListe
'u'
Spezifikation:
akzeptorP
['0', '1', '0']
eListe
Fal
se
Slide 75
Aufgabe
75
Tipp:
Verallgemeinern Sie die unten an
konkreten Beispielen gezeigten
Problemreduktionen.
0
0
1
g
u
1
Fall 1: Verarbeite eine leere Eingabenliste
simulartorP('g', []) 'g'
Fall 2: Verarbeite eine nicht-leere Eingabenliste
simulatorP('g', ['1', '0', '0']) simulatorP('u', ['0', '0'])]
u
'u'
Slide 76
Aufgabe
76
Entwickeln Sie eine Funktion zur Realisierung eines Ampelsimulators.
Spezifikation:
simulatorA
'ro'
['t', 't', 't', 't']
z
eListe
[OOo, ooO, oOo, Ooo]
Slide 77
Lösungsvorschlag
77
Fall 1: Verarbeite eine leere Eingabenliste
simulartorP('g', []) 'g'
Fall 2: Verarbeite eine nicht-leere Eingabenliste
simulatorP('g', ['1', '0', '0']) simulatorP('u', ['0', '0'])]
u
'u'
def simulatorP(z, eListe):
if len(eListe) == 0:
return z
else:
return simulatorP(deltaP(z, eListe[0]), eListe[1:])
def akzeptorP(eListe):
return enzustand(simulatorP(anfangszustand(), eListe))
Slide 78
78
Weitere Ideen
Entwickeln Sie ein System, bei dem die Arbeitsweise eines Transduktors
(Automat mit Ausgabe) simuliert wird.
Slide 79
79
Schritt 3: Automat als Eingabe
Es soll ein universelles Akzeptor-System entwickelt werden, mit dem eine
Folge von Eingaben mit einem beliebig vorgegebenen Automaten
verarbeiten werden kann.
def anfangszustandP():
...
def endzustandP(z):
...
def deltaP(z, e):
...
Ok!
00011011
Slide 80
Aufgabe
80
Verallgemeinern Sie das bisher entwickelte Simulator-System. Benutzen Sie
die folgende Vereinbarung:
Eine Automatenbeschreibung ist ein Tripel (az, ez, de) mit:
- az ist eine Funktion zur Beschreibung des Anfangszustands.
- ez ist eine Funktion zur Beschreibung der Endzustände.
- de ist eine Überführungsfunktion, die jedem Zustand und jeder Eingabe
aus einen neuen Zustand zuordnet.
Spezifikation:
Funktionen
simulator
(anfangszustandP, enzustandP, deltaP)
'g'
['0', '1', '0']
aut
z
eListe
'u'
Slide 81
81
Lösungsvorschlag
def simulator(aut, z, eListe):
if len(eListe) == 0:
return z
else:
return simulator(aut, aut[2](z, eListe[0]), eListe[1:])
def akzeptor(aut, eListe):
return aut[1](simulator(aut, aut[0](), eListe))
Slide 82
82
Weitere Ideen
Entwickeln Sie ein System, bei dem die Arbeitsweise eines beliebigen
Transduktors simuliert wird.
Slide 83
83
Teil 5
Miniprojekt: Programminterpreter
Slide 84
84
Miniprojekt "Programminterpreter"
Ziel ist es, ein System zur Ausführung einfacher imperativer Programme zu
entwickeln.
{b: 2; u: 5}
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
{b: 256; u: 0; p: 32}
Interpreter
Slide 85
Problemvereinfachung
85
Ausgangszustand
Zuweisungsprogramm
Endzustand
{x: 2; y: 5}
BEGIN
z := x;
x := y;
y := z
END
Interpreter
{x: 5; y: 2; z: 5}
Wir betrachten zunächst nur Programme, die aus "primitiven Zuweisungen"
vom Typ := bestehen.
Slide 86
Funktionale Modellierung
86
Ausgangszustand
Zuweisungsprogramm
Endzustand
{x: 2; y: 5}
BEGIN
z := x;
x := y;
y := z
END
Interpreter
{x: 5; y: 2; z: 5}
Spezifikation:
PrimZuwSeqAusfuehren
[(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')]
[('x', 2), ('y', 5)]
zuweisungen
zustand
[('x', 5), ('y', 2), ('z', 5)]
Slide 87
87
Schritt 1: Variablenzustände
Variablenzustände beschreiben die jeweils aktuellen Variablenbelegungen.
Zur Verarbeitung einer primitiven Zuweisung wie z := x muss der Wert
einer Variablen (hier x) bzgl. des aktuellen Variablenzustands ermittelt
werden und der Wert einer Variablen (hier y) im Variablenzustand verändert
(oder neu angelegt) werden.
{x: 2; y: 5}
z := x;
{x: 2; y: 5; z: 2}
x := y;
{x: 5; y: 5; z: 2}
y := z
{x: 5; y: 2; z: 2}
Slide 88
88
Aufgabe
Modellieren Sie geeignete Funktionen, mit denen der Wert einer Variablen
bzgl. eines Variablenzustands ermittelt werden kann und der Wert einer
Variablen im Variablenzustand verändert (oder neu angelegt) werden kann.
Entwickeln Sie anschließend mit Hilfe rekursiver
Problemreduktionsschemata geeignete Funktionsdeklarationen.
Slide 89
Lösungsvorschlag
89
Spezifikation:
VariablenWert
'y'
[('x', 2), ('y', 5)]
bezeichner
zustand
5
Spezifikation:
NeuerZustand
'z'
2
[('x', 2), ('y', 5)]
bezeichner
wert
zustand
[('x', 2), ('y', 5), ('z', 2)]
NeuerZustand
'x'
5
[('x', 2), ('y', 5), ('z', 2)]
bezeichner
wert
zustand
[('x', 5), ('y', 5), ('z', 2)]
Slide 90
90
Lösungsvorschlag
def VariablenWert(bezeichner, zustand):
if len(zustand) == 0:
return '?'
else:
if bezeichner == zustand[0][0]:
return zustand[0][1]
else:
return VariablenWert(bezeichner, zustand[1:])
def NeuerZustand(bezeichner, wert, zustand):
if len(zustand) == 0:
return [(bezeichner, wert)]
else:
if bezeichner == zustand[0][0]:
return [(bezeichner, wert)] + zustand[1:]
else:
return [zustand[0]] +
NeuerZustand(bezeichner, wert, zustand[1:])
Slide 91
91
Schritt 2: Zuweisungsinterpreter
Mit Hilfe geeigneter Funktionen sollen jetzt einzelne primitive Zuweisungen
bzw. Sequenzen primitiver Zuweisungen ausgeführt werden.
{x: 2; y: 5}
z := x;
{x: 2; y: 5; z: 2}
x := y;
{x: 5; y: 5; z: 2}
y := z
{x: 5; y: 2; z: 2}
Slide 92
92
Aufgabe
Modellieren und implementieren Sie die hierzu erforderlichen Funktionen.
Slide 93
93
Lösungsvorschlag
Spezifikation:
PrimZuwAusfuehren
(':=', 'z', 'y')
[('x', 2), ('y', 5)]
zuweisung
zustand
[('x', 2), ('y', 5), ('z', 2)]
Spezifikation:
PrimZuwSeqAusfuehren
[(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')]
[('x', 2), ('y', 5)]
zuweisungen
zustand
[('x', 5), ('y', 2), ('z', 5)]
Slide 94
94
Lösungsvorschlag
def PrimZuwAusfuehren(zuweisung, zustand):
return NeuerZustand(zuweisung[1],
VariablenWert(zuweisung[2], zustand), zustand)
def PrimZuwSeqAusfuehren(zuweisungen, zustand):
if len(zuweisungen) == 0:
return zustand
else:
return PrimZuwSeqAusfuehren(zuweisungen[1:],
PrimZuwAusfuehren(zuweisungen[0], zustand))
Slide 95
95
Schritt 3: Terminterpreter
Auf der rechten Seite einer Zuweisung sollen jetzt auch beliebige
Rechenterme erlaubt sein. Wir beschränken uns auf das Rechnen mit
ganzen Zahlen. Als Rechenoperationen sollen daher +, -, *, / (Division ohne
Rest), % (Rest bei der Division) betrachtet werden.
{x: 2; y: 5}
x := x-y;
{x: -3; y: 5}
y := x+y;
{x: -3; y: 2}
x := y-x
{x: 5; y: 2}
Slide 96
96
Aufgabe
Ergänzen Sie die bereits begonnene Funktionsdeklaration und
verallgemeinern Sie den Zuweisungsinterpreter. Beachten Sie die hier
gewählte Darstellung von Termen, z. B.:
z+(x-y) wird dargestellt durch ('+', 'z', ('-', 'x', 'y'))
x+1 wird dargestellt durch ('+', 'x', 1)
def TermWert(term, zustand):
if isinstance(term, int):
return term
else:
if not isinstance(term, tuple):
return VariablenWert(term, zustand)
else:
if term[0] == '+':
return TermWert(term[1], zustand) +
TermWert(term[2], zustand)
else:
...
Slide 97
97
Schritt 4: Bedingungsinterpreter
Als nächstes sollen Anweisungen mit Bedingungen (wie z. B. if (x > 0) then
...) betrachtet werden. Hierzu muss der Interpreter Bedingungen auswerten
können. Der Einfachheit halber betrachten wir nur Bedingungen vom Typ
. Logische Verknüpfungen von Bedingungen
sollen also keine Rolle spielen.
Spezifikation:
BooleWert
('>', 'y', ('+', 'x', 'x'))
[('x', 2), ('y', 5)]
term
zustand
True
Slide 98
98
Aufgabe
Entwickeln Sie analog zur Auswertung von Rechentermen eine Funktion zur
Auswertung von Bedingungen.
Slide 99
99
Schritt 5: Kontrollinterpreter
Ziel ist es, Fallunterscheidungs- und Wiederholungsanweisungen
auszuführen, wie sie etwa im unten dargestellten Programm vorkommen.
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
Slide 100
100
Aufgabe
Überlegen Sie sich eine Darstellung von Kontrollanweisungen (wie im
Programm) mit den von Python zur Verfügung gestellten Datenstrukturen.
Entwickeln Sie geeignete Funktionen zur Ausführung dieser Anweisungen.
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
Slide 101
101
Lösungsvorschlag
Fallunterscheidung:
('if', (..Bedingung..), [..Then-Anweisungen..], [..Else-Anweisungen..])
Wiederholung:
('while', (..Bedingung..), [..Anweisungen..])
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
[
(':=', 'p', 1),
('while', ('>', 'u', 0),
[
('if', ('==', ('%', 'u', 2), 1),
[
(':=', 'u', ('-', 'u', 1)),
(':=', 'p', ('*', 'p', 'b'))
],[]),
(':=', 'u', ('/', 'u', 2)),
(':=', 'b', ('*', 'b', 'b'))
])
]
Slide 102
102
Lösungsvorschlag
def AnwAusfuehren(anweisung, zustand):
if anweisung[0] == ':=':
return NeuerZustand(anweisung[1],
TermWert(anweisung[2], zustand), zustand)
else:
if anweisung[0] == 'if':
if BooleWert(anweisung[1], zustand):
return AnwSeqAusfuehren(anweisung[2], zustand)
else:
return AnwSeqAusfuehren(anweisung[3], zustand)
else:
if anweisung[0] == 'while':
if BooleWert(anweisung[1], zustand):
return AnwAusfuehren(anweisung,
AnwSeqAusfuehren(anweisung[2], zustand))
else:
return zustand
Slide 103
103
Lösungsvorschlag
def AnwSeqAusfuehren(anweisungen, zustand):
if len(anweisungen) == 0:
return zustand
else:
return AnwSeqAusfuehren(anweisungen[1:],
AnwAusfuehren(anweisungen[0], zustand))
Slide 104
104
Lösungsvorschlag
def ProgrammAusfuehren(programm, zustand):
return AnwSeqAusfuehren(programm, zustand)
Beachten Sie, dass ein Programm hier immer eine Anweisungssequenz ist,
auch wenn es nur aus einer Anweisung besteht.
Slide 105
105
Teil 6
Deklarative Programmierung
Slide 106
106
Ein Problem - zwei Lösungen
Problem:
Wie fügt man die Elemente einer Listen (die alle Zeichenketten sein sollen)
zu einer einzigen Zeichenkette zusammen?
Lösung:
Lösung:
Stell eine Hilfsliste "ergebnis" wie folgt
zusammen:
Starte mit der einer leeren Liste.
Füge Schritt für Schritt alle Elemente der
Liste jeweils am Ende der Hilfsliste ein.
Die am Schluss erhaltene Hilfsliste ist die
gesuchte Liste.
Wenn die Liste leer ist, dann ist die leere
Liste bereits das Ergebnis. Wenn die Liste
nicht leer ist, dann erhält man das Ergebnis
wie folgt: Man fügt das erste Element
vorne an die Zeichenkette, die man erhält,
wenn man alle Elemente der Restliste
zusammenfügt.
def zusammenfuegen(liste):
ergebnis = ''
for wort in liste:
ergebnis = ergebnis + wort
return ergebnis
def zusammenfuegen(liste):
if len(liste) == 0:
return ''
else:
return liste[0] + zusammenfuegen(liste[1:])
Imperativer Ansatz:
Man beschreibt Schritt für Schritt den
Vorgang, wie man alle Elemente der Liste
zusammenfügt.
Deklarativer Ansatz:
Man beschreibt, welche Eigenschaften das
Ergebnis haben soll, das man beim
Zusammenfügen erhält.
Slide 107
107
Imperative Programmierung
Imperative Programmierung besteht darin, eine (mehr oder weniger
abstrakte) Maschine mit Hilfe von Anweisungen zu steuern.
A.-Zustand
{liste: ['HA', 'LL', 'O']}
Anweisungen
def zusammenfuegen(liste):
ergebnis = ''
for wort in liste:
ergebnis = ergebnis + wort
return ergebnis
E.-Zustand
{ergebnis: 'HALLO']}
Registermaschine
Ansatz: Beschreiben, 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.
Slide 108
108
Deklarative Programmierung
Deklarative Programmierung besteht darin, den Problemkontext
(Miniwelt) mit gegebenen Mitteln (hier: Funktionen) zu beschreiben.
Term
zusammenfuegen(['HA', 'LL', 'O'])
def zusammenfuegen(liste):
if len(liste) == 0:
Deklarationen
return ''
else:
return liste[0] + zusammenfuegen(liste[1:])
Ergebnis
Reduktionsmaschine
'HALLO'
Ansatz: Beschreiben, was in der Modellwelt gelten soll
Die funktionale Programmierung arbeitet 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.
Slide 109
109
Fazit
Funktionale Programmierung erfolgt auf einem höheren Abstraktionsniveau:
- keine Anweisungen an eine Maschine,
- sondern Beschreibung funktionaler Zusammenhänge.
Konsequenzen:
- Funktionale Programme sind kurz.
- Funktionale Programme sind leicht zu verstehen.
- Funktionale Programmierung ist wenig fehleranfällig.
- Funktionale Programmierung eignet sich zum „Prototyping“.
Slide 110
110
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)
[Fischbacher 97] T. Fischbacher: Funktionale Programmierung. In: LOG IN 17 (1997) Heft 3 /
4, S. 24-26.
[ISB 97] Staatliches Institut für Schulpädagogik und Bildungsforschung München (Hrsg.):
Funktionales Programmieren in Gofer. Baustein zur Didaktik der Informatik. München, 1997.
[Puhlmann 98] H. Puhlmann: Funktionales Programmieren - Eine organische Verbindung von
Informatikunterricht und Mathematik. In: LOG IN 18 (1998) Heft 2, S. 46-50.
[Schwill 93] A. Schwill: Funktionale Programmierung mit Caml. In: LOG IN 13 (1993) Heft 4,
S. 20-30.
[Wagenknecht 94] Christian Wagenknecht: Rekursion. Ein didaktischer Zugang mit
Funktionen. Bonn: Dümmlers Verlag 1994.
[Wolff von Gudenberg 96] J. Wolff. von Gudenberg: Algorithmen, Datenstrukturen, Funktionale
Programmierung. Eine praktische Einführung mit Caml Light. Bonn: Addison-Wesley 1996.
Funktionale Programmierung
Klaus Becker
2007
Slide 2
2
Programmieren mit Funktionen
Mit Funktionen kann man programmieren. Die
Programme bestehen aus Funktionsdeklarationen.
Funktionsdeklarationen werden mit Hilfe von
Funktionskomposition, Fallunterscheidungen und
Rekursion aufgebaut. ...
Zielsetzung: Einblick in die funktionale Programmierung gewinnen
Grundideen verstehen
Grundkonzepte kennen lernen
Relevanz anhand von Miniprojekten erkennen
Slide 3
3
Teil 1
Von der Registermaschine
zur funktionalen Abstraktion
Slide 4
Registermaschine
4
Eine Registermaschine bearbeitet beliebig eingebbare Daten nach einem
fest vorgegebenen Programm.
Daten
1:
2:
3:
4:
5:
5
3
0
0
0
..
> 1
2
3
4
5
6
JMP
INC
DEC
TST
JMP
HLT
4
1
2
2
2
Programm
> 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.
Slide 5
Aufgabe
5
Was leistet das unten abgebildete Registermaschinenprogramm?
Messen Sie die Zeit, die Sie brauchen, um das herauszufinden.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
Slide 6
6
Aufgabe
Was leistet das unten abgebildete (Python-) Programm?
Messen Sie die Zeit, die Sie brauchen, um das herauszufinden.
while (R2 <> 0) and (R3 <> 0):
R1 = R1 + 1
R2 = R2 - 1
R3 = R3 - 1
while R2 <> 0:
R2 = R2 - 1
while R3 <> 0:
R3 = R3 -1
Slide 7
7
Spaghetti-Code mit Sprunganweisungen
Durch die vielen Sprunganweisungen verliert man leicht
den Überblick über die Ablauflogik.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
Wer strukturiert programmiert, verzichtet auf solche
Sprunganweisungen, auch wenn die
Programmiersprache sie zur Verfügung stellt.
Viele Programmiersprachen stellen keine
Sprunganweisungen zur Verfügung. Dann können solch
schwer zu durchschauenden Programme gar nicht erst
geschrieben werden.
"Spaghetti-Code"
Slide 8
8
Ablaufkontrolle mit Kontrollanweisungen
An Stelle von Sprunganweisungen benutzt man Kontrollstrukturen, um die
Ablauflogik strukturiert zu beschreiben.
1 TST 2
2 JMP 4
3 JMP 7
4 TST 3
5 JMP 13
6 JMP 10
7 TST 3
8 JMP 19
9 HLT
10 TST 2
11 JMP 17
12 HLT
13 DEC 2
14 DEC 3
15 INC 1
16 JMP 1
17 DEC 2
18 JMP 10
19 DEC 3
20 JMP 7
while (R2 <> 0) and (R3 <> 0):
R1 = R1 + 1
R2 = R2 - 1
R3 = R3 - 1
while R2 <> 0:
R2 = R2 - 1
while R3 <> 0:
R3 = R3 -1
Ablaufbeschreibung mit Kontrollstrukturen
Ablaufbeschreibung mit Sprunganweisungen
Slide 9
9
Aufgabe
Vergleichen Sie die beiden Programme. Welche Version ist besser?
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, 7)
>>> m
5
def minimum(a, b):
m = 0
while (a <> 0) and (b <> 0):
m = m + 1
a = a - 1
b = b - 1
return m
>>> minimum(5, 7)
5
Slide 10
Seiteneffekte
10
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
>>>
>>>
>>>
8
>>>
>>>
>>>
5
m = 3
minimum(5, 7)
m
m = 0
minimum(5, 7)
m
Unterprogramm mit Seiteneffekt
def minimum(a, b):
m = 0
while (a <> 0) and (b <> 0):
m = m + 1
a = a - 1
b = b - 1
return m
>>> minimum(5, 7)
5
Slide 11
11
Programme ohne Seiteneffekte
Eine Funktion soll normalerweise aus den Argumenten einen Wert
berechnen und nichts anderes nebenbei tun. Von diesem Prinzip weichen
Programmierer jedoch immer wieder ab, etwa indem sie in der Funktion
globale Variablen verändern. Wer strukturiert programmiert, verzichtet auf
Seiteneffekte.
Es gibt Programmiersprachen, die keine Seiteneffekte zulassen. Die Idee
dabei ist, auf Wertzuweisungen zu verzichten, so dass keine
unbeabsichtigten Seiteneffekte möglich sind.
def minimum(a, b):
if a == 0:
return a
else:
if b == 0:
return b
else:
return 1 + minimum(a-1, b-1)
Unterprogramm ohne Wertzuweisung
Slide 12
12
Zielsetzung
Ziel ist es, die Grundideen funktionaler Programmierung anhand von
Beispielen problemorientiert zu erarbeiten. Insbesondere soll dabei
herausgearbeitet werden, dass Programmierung ohne Wertzuweisungen
möglich ist.
Slide 13
13
Teil 2
Konzepte der funktionalen Programmierung
Slide 14
Fallstudie: Chiffrieren
14
Ziel ist es, Chiffrierverfahren (die auf modularem Rechnen basieren), mit
Hilfe funktionaler Programme zu beschreiben.
PYLZFOWBNQCYBUVNCBLGYC
HYAYBYCGMWBLCZNYHNTCZY
LN
VDOYH
FDHVDU
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
D E F G H I J K L M N O P Q R S T U V W X Y Z A B C
Quelltext:
SALVECAESAR
Schlüssel: D
Geheimtext:
VDOYHFDHVDU
Slide 15
15
Additives Chiffrierverfahren
Codierung:
Code: A → 1
Blocklänge: 2
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
Decodierung:
Code: A → 1
Blocklänge: 2
AA → 0101
AB → 0102
...
ZZ → 2626
0119#2005#1809#24
z → (z + d) % m
0119#2005#1809#24
Bed.: z < m
m > maxCode
z → (z + e) % m
Bed.:
(d + e) % m = 0
AA → 0101
AB → 0102
...
ZZ → 2626
AS#TE#RI#X
2221#1107#1010#2126
2221#1107#1010#2126
0119#2005#1809#24
0119#2005#1809#24
AS#TE#RI#X
Slide 16
16
Additives Chiffrierverfahren
Wir betrachten zunächst das Verschlüsseln und Entschlüsseln von
Zahlenfolgen mit Schlüsseln, die aus zwei Komponenten bestehen. Da Verund Entschlüsseln gleich funktionieren, reicht es, nur das Verschlüsseln zu
betrachten.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
z → (z + d) % m
0119#2005#1809#24
Bed.: m ist größer als
die maximale Codezahl
2221#1107#1010#2126
z → (z + e) % m
2221#1107#1010#2126
Bed.:
d+e=m
0119#2005#1809#24
Slide 17
Funktionale Modellierung
17
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Eingaben:
zahl: die zu verschlüsselnde Zahl
schluessel: Zahlenpaar bestehend aus der zu addierenden Konstante und dem Divisionsmodul
Ausgabe:
die berechnete verschlüsselte Zahl
Slide 18
Funktionale Modellierung
18
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Eingaben:
zahlenListe: Liste mit den zu verschlüsselnden Zahlen
schluessel: Zahlenpaar bestehend aus der zu addierenden Konstante und dem Divisionsmodul
Ausgabe:
Liste mit den berechneten verschlüsselten Zahlen
Slide 19
Funktionales Programm
19
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Implementierung:
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
Slide 20
Funktionales Programm
20
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
0119#2005#1809#24
2221#1107#1010#2126
Spezifikation:
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Implementierung:
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 21
Datenstruktur "Tupel"
21
Tupel sind unveränderbare Sequenzen. Man verwendet Tupel, wenn man
ein Objekt repräsentieren möchte, das aus mehreren Komponenten
besteht.
Spezifikation:
Tupel
verschluesselnZahl
119
(2102, 3000)
zahl
schluessel
2221
Implementierung:
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
Zugriff auf die Komponenten
Slide 22
Datenstruktur "Liste"
22
Listen sind veränderbare Sequenzen. Sie können (in Python) Objekte
beliebigen Typs enthalten. Mit Listen kann man komplexere Strukturen
modellieren.
Spezifikation:
Liste
verschluesseln
[119, 2005, 1809, 24]
(2102, 3000)
Implementierung:
zahlenListe
schluessel
Liste
[2221, 1107, 1010, 2126]
Listenoperationen
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 23
23
Listenoperationen
Das Python-Protokoll zeigt die Listenoperationen, die im Folgenden benötigt
werden. Weitere Listenoperationen findet man in der Dokumentation.
>>> zahlenListe = []
>>> zahlenListe
[]
>>> len(zahlenListe)
0
>>> zahlenListe = [119, 2005, 1809, 24]
>>> zahlenListe
[119, 2005, 1809, 24]
>>> zahlenListe[0]
119
>>> zahlenListe[1:]
[2005, 1809, 24]
>>> [zahlenListe[0]] + zahlenListe[1:]
[119, 2005, 1809, 24]
>>>
...
leere Liste
Länge einer Liste
Zugriff auf Listenelemente
Zugriff auf eine Restliste
Konkatenation von Listen
Slide 24
24
Aufgabe
Testen Sie die im funktionalen Programm vorkommenden
Listenoperationen. Variieren Sie dabei auch die vorkommenden
Parameterwerte.
>>> zahlenListe = []
>>> zahlenListe
[]
>>> len(zahlenListe)
0
>>> zahlenListe = [119, 2005, 1809, 24]
>>> zahlenListe
[119, 2005, 1809, 24]
>>> zahlenListe[0]
119
>>> zahlenListe[1:]
[2005, 1809, 24]
>>> [zahlenListe[0]] + zahlenListe[1:]
[119, 2005, 1809, 24]
>>>
...
Slide 25
Rekursive Problemreduktion
25
Rekursive Problemreduktion: Reduziere des Problems auf ein
entsprechendes, aber „verkleinertes“ Problem.
Fall 1: Bearbeite eine leere Liste
verschluesseln([], (2102, 3000))
→ []
Rekursionsanfang:
Löse das Problem direkt
[]
Fall 2: Bearbeite eine nicht-leere Liste
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
[2221, 1107, 1010, 2126]
→ [verschluesselnZahl(119, (2102, 3000))] +
[2221]
verschluesseln([2005, 1809, 24], (2102, 3000))
[1107, 1010, 2126]
Rekursionsschritt:
Löse ein entsprechendes
Problem
Slide 26
26
Aufgabe
Fügen Sie Ausgabeanweisungen ein und verfolgen Sie so die Auswertung
der einzelnen Funktionsaufrufe.
def verschluesseln(zahlenListe, schluessel):
print zahlenListe, schluessel
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Slide 27
27
Kontrollstruktur "Rekursion"
Rekursion wird in der funktionalen Programmierung als Kontrollstruktur
benutzt, um wiederkehrende Berechnungen durchzuführen.
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
→ [verschluesselnZahl(119, (2102, 3000))] +
verschluesseln([2005, 1809, 24], (2102, 3000))
→ [2221] +
verschluesseln([2005, 1809, 24], (2102, 3000))
→ [2221] +
[verschluesselnZahl(2005, (2102, 3000))] +
verschluesseln([1809, 24], (2102, 3000))
→ [2221] +
[1107] +
verschluesseln([1809, 24], (2102, 3000))
...
Slide 28
28
Kontrollstruktur "Rekursion"
→ [2221] +
[1107] +
[verschluesselnZahl(1809, (2102, 3000))] +
verschluesseln([24], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
verschluesseln([24], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[verschluesselnZahl(24, (2102, 3000))] +
verschluesseln([], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[2126] +
verschluesseln([], (2102, 3000))
→ [2221] +
[1107] +
[1010] +
[2126] +
[]
→ [2221, 1107, 1010, 2126]
Slide 29
Kontrollstrukturen
29
Funktionsdeklarationen werden in der funktionalen Programmierung mit
Hilfe von
- Funktionskomposition,
- Fallunterscheidungen und
- Rekursion
aufgebaut. Diese Strukturen legen letztlich die Ablaufkontrolle fest.
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Fallunterscheidung
Rekursion
Funktionskomposition
Slide 30
Aufgabe
30
Zur Übung rekursiver Funktionsdeklarationen sollen folgende Funktionen
implementiert werden. Gehen Sie analog zur Funktion "verschluesseln" vor.
Spezifikation:
addieren
[6, 13, 0, 5]
7
zahlenListe
konstante
[13, 20, 7, 12]
Spezifikation:
nullErsetzen
[6, 0, 13, 0, 0, 5, 3]
7
zahlenListe
konstante
[6, 7, 13, 7, 7, 5, 3]
Slide 31
31
Aufgabe
In der Datei "ChiffriersystemModularesAddierenRekursiv.py" finden Sie eine
Implementierung des Chiffriersystems basierend auf modularer Addition,
die (außer zum Testen) keine Wertzuweisungen benutzt. Analysieren Sie die
Funktionsdeklarationen auch im Hinblick auf die vorkommenden
Kontrollstrukturen.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (2102, 3000)
Entschlüsselung:
privater Schlüssel
(e, m) = (898, 3000)
z → (z + d) % m
Bed.: z < m
m > maxCode
z → (z + e) % m
Bed.:
(d + e) % m = 0
0119#2005#1809#24
2221#1107#1010#2126
2221#1107#1010#2126
0119#2005#1809#24
Slide 32
32
Aufgabe
Ändern Sie die entsprechenden Funktionsdeklarationen so ab, dass man das
multiplikative Chiffrierverfahren erhält.
Verschlüsselung:
öffentlicher Schlüssel
(d, m) = (7, 30)
Entschlüsselung:
privater Schlüssel
(e, m) = (13, 30)
z → (z * d) % m
Bed.:
z
Bed.:
(d * e) % m = 1
01#19#20#05#18#09#24
07#13#20#05#06#03#18
07#13#20#05#06#03#18
01#19#20#05#18#09#24
Slide 33
33
Codeduplizierung
Die Implementierungen zur Verschlüsselung mit modularer Addition und
modularer Multiplikation weisen auffallende Ähnlichkeiten auf.
def verschluesselnZahlAdd(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesselnAdd(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahlAdd(zahlenListe[0], schluessel)] +
verschluesselnAdd(zahlenListe[1:], schluessel)
def verschluesselnZahlMul(zahl, schluessel):
return (zahl * schluessel[0]) % schluessel[1]
def verschluesselnMul(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahlMul(zahlenListe[0], schluessel)] +
verschluesselnMul(zahlenListe[1:], schluessel)
Slide 34
Funktionen als Eingabeobjekte
34
Codeduplizierung lässt sich vermeiden, wenn man Funktionen als
Eingabeobjekte von Funktionen zulässt.
Spezifikation:
Funktion
verschluesseln
z → (z + d) % m
[119, 2005, 1809, 24]
(2102, 3000)
Liste
Tupel
verfahren
zahlenListe
schluessel
[2221, 1107, 1010, 2126]
Slide 35
35
Funktionen als Eingabeobjekte
Die Implementierung zeigt, dass man Funktionen hier wie andere Objekte
auch als Parameter übergeben kann.
def verschluesselnZahlAddieren(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesselnZahlMultiplizieren(zahl, schluessel):
return (zahl * schluessel[0]) % schluessel[1]
def verschluesselnZahl(verfahren, zahl, schluessel):
return verfahren(zahl, schluessel)
def verschluesseln(verfahren, zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(verfahren, zahlenListe[0], schluessel)] +
verschluesseln(verfahren, zahlenListe[1:], schluessel)
def verschluesselnModularesAddieren(zahlenListe, schluessel):
return verschluesseln(verschluesselnZahlAddieren, zahlenListe, schluessel)
def verschluesselnModularesMultiplizieren(zahlenListe, schluessel):
return verschluesseln(verschluesselnZahlMultip.., zahlenListe, schluessel)
Slide 36
36
Aufgabe
Testen Sie die Implementierung in der Datei
"ChiffriersystemModularesRechnenRekursiv.py".
Erweitern Sie diese Implementierung um die Möglichkeit, Verschlüsselung
auch mit modularem Potenzieren durchzuführen.
Slide 37
37
Funktionale Programmierung
Mit Funktionen kann man programmieren. Die
Programme bestehen aus Funktionsdeklarationen.
Ein Programmaufruf erfolgt mit einem
funktionalen Berechnungsausdruck.
Funktionsdeklaration
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
verschluesseln([119, 2005, 1809, 24], (2102, 3000))
Berechnungsausdruck
Slide 38
38
Funktionale Programmierung
Funktionsdeklarationen werden mit Hilfe von
- Funktionskomposition,
- Fallunterscheidungen und
- Rekursion
aufgebaut.
Funktionskomposition
def verschluesselnZahl(zahl, schluessel):
return (zahl + schluessel[0]) % schluessel[1]
def verschluesseln(zahlenListe, schluessel):
if len(zahlenListe) == 0:
return []
Rekursion
else:
return [verschluesselnZahl(zahlenListe[0], schluessel)] +
verschluesseln(zahlenListe[1:], schluessel)
Fallunterscheidung
Slide 39
39
Funktionale Programmierung
Objekte können mit Hilfe von Tupelbildung und
Listen zu neuen Einheiten zusammengefasst
werden.
Funktionen können als Eingabeobjekte für weitere
Funktionen benutzt werden.
Funktion
Tupel
def verschluesselnZahl(verfahren, zahl, schluessel):
return verfahren(zahl, schluessel)
def verschluesseln(verfahren, zahlenListe, schluessel):
if len(zahlenListe) == 0:
Liste
return []
else:
return [verschluesselnZahl(verfahren, zahlenListe[0], schluessel)] +
verschluesseln(verfahren, zahlenListe[1:], schluessel)
Slide 40
40
Miniprojekte
Funktionale Programmierung eignet sich sehr gut, um schnell ein System zu
entwickeln und zu testen. Man konzentriert sich nur auf die "Logik" des
Systems, nicht auf "schmückendes Beiwerk". Die Programme sollen dabei
kurz und gut überschaubar sein.
Im Folgenden soll diese Vorgehensweise anhand von drei Miniprojekten
aufgezeigt werden.
Slide 41
41
Miniprojekt "Geometrische Abbildungen"
Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische
Operationen durchführen kann.
Slide 42
42
Miniprojekt "Automatensimulator"
Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines
beliebigen Automaten simulieren kann.
0
0
akzeptor
1
g
u
Ok!
1
00011011
Slide 43
43
Miniprojekt "Programminterpreter"
Ziel ist es, ein System zur Ausführung einfacher imperativer Programme zu
entwickeln.
{b: 2; u: 5}
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
{b: 256; u: 0; p: 32}
Interpreter
Slide 44
44
Aufgabe
Wählen Sie eines der Miniprojekte aus und realisieren Sie das skizzierte
System. Sie können versuchen, das System völlig selbständig zu entwickeln,
oder die im Folgenden angebotenen Hilfen zu nutzen.
Slide 45
45
Teil 3
Miniprojekt: Geometrische Abbildungen
Slide 46
46
Geometrische Abbildungen
Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische
Operationen durchführen kann.
Slide 47
47
Schritt 1: Verschiebungen
Verschiebungen können mit Hilfe von Vektoren dargestellt werden. Die
Abbildung von Punkten lässt sich dann durch Addition von Vektoren
realisieren.
Vieleck:
A(0, 0), B(40, 0), C(40, 30), D(0, 30)
Verschiebung mit Vektor:
v=
10
10
verschobenes Vieleck:
A'(10, 10), B'(50, 10), C'(50, 40), D'(10, 40)
Slide 48
48
Listenrepräsentation
Wir repräsentieren Vektoren mit Hilfe von Listen. Ebenso stellen wir Punkte
und Punktfolgen mit Hilfe von Listen dar.
Vieleck:
[[0, 0], [40, 0], [40, 30], [0, 30]]
Verschiebung mit Vektor [10, 10]:
[0, 0] + [10, 10] [10, 10]
[40, 0] + [10, 10] [50, 10]
[40, 30] + [10, 10] [50, 40]
[0, 30] + [10, 10] [10, 40]
verschobenes Vieleck:
[[10, 10], [50, 10], [50, 40], [10, 40]]
Slide 49
Aufgabe
49
Entwickeln Sie ein funktionales Programm für die Addition von Vektoren.
Um das Programm flexibel benutzen zu können, sollen Vektoren beliebig
viele Komponenten haben können.
Spezifikation:
add
[60, 30, 40]
v
[10, 0, 30]
w
[70, 30, 70]
Slide 50
Aufgabe
50
Realisieren Sie jetzt die Abbildung "Verschieben" mit Hilfe der bereits
implementierten Vektoraddition.
Spezifikation:
verschiebenPunkt
[40, 30]
punkt
[10, 10]
vektor
[50, 30]
Spezifikation:
verschieben
[[20, 10], [40, 30], [70, 0]]
vieleck
[10, 10]
vektor
[[30, 20], [50, 40], [80, 10]]
Slide 51
Lösungsvorschlag
51
Spezifikation:
add
[60, 30, 40]
v
[10, 0, 30]
w
[70, 30, 70]
Implementierung:
def add(v, w):
if len(v) == 0:
if len(w) == 0:
return []
else:
return w
else:
if len(w) == 0:
return v
else:
return [v[0] + w[0]] + add(v[1:], w[1:])
Slide 52
Lösungsvorschlag
52
Spezifikation:
verschiebenPunkt
[40, 30]
punkt
[10, 10]
vektor
Spezifikation:
[50, 30]
verschieben
[[20, 10], [40, 30], [70, 0]]
vieleck
[10, 10]
vektor
[[30, 20], [50, 40], [80, 10]]
def verschiebenPunkt(punkt, vektor):
return add(punkt, vektor)
def verschieben(vieleck, vektor):
if len(vieleck) == 0:
return []
else:
return [verschiebenPunkt(vieleck[0], vektor)] +
verschieben(vieleck[1:], vektor)
Slide 53
53
Test mit Turtlegrafik
Testen Sie die einzelnen
Funktionsdeklarationen mit Hilfe
geeigneter Testfälle.
Zur visuellen Kontrolle können Sie die
geometrischen Objekte auch mit Hilfe
von Turtlegrafik darstellen. Kopieren
Sie zu diesem Zweck die Daten
"xturtle.py" (von G. Lingl aus dem
Buch "Python für Kids") in die
Standardbibliothek von Python.
Benutzen Sie die Hilfsfunktion zum
Zeichnen von Vielecken (siehe
"GeometrischeOperationen1.py").
Beachten Sie, dass diese Hilfsfunktion
Seiteneffekte in Form von Grafiken
erzeugt und damit nicht mehr rein
funktional ist.
Slide 54
54
Test mit Turtlegrafik
def zeichneVieleck(punkte):
# wird nur zum Zeichnen benutzt
if len(punkte) > 0:
penup()
setpos(punkte[0])
pendown()
streckenzug = punkte[1:] + [punkte[0]]
for punkt in streckenzug:
goto(punkt)
if __name__ == "__main__":
# Test der einzelnen Funktionen
import doctest
doctest.testmod(verbose=True)
# Visueller Test mit Turtlegrafik
from xturtle import *
reset()
vieleck1 = [[0, 0], [40, 0], [40, 30], [0, 30]]
vieleck2 = verschieben(vieleck1, [10, 10])
zeichneVieleck(vieleck1)
zeichneVieleck(vieleck2)
Slide 55
55
Schritt 2: Streckungen
Streckungen lassen sich durch
Multiplikation eines Vektors mit
einer Zahl realisieren.
Fall1: Streckzentrum im Ursprung
Streckung mit dem Faktor k:
multipliziere die Koordinaten der Punkte mit
dem Streckfaktor k
Fall 2: Streckzentrum beliebig
Verschiebe erst das Streckzentrum in den
Ursprung, führe die Steckung aus und
verschiebe wieder zurück an den
Ausgangspunkt
Beispiel:
Vieleck:
A(30, 0), B(70, 0), C(70, 30), D(30, 30)
Streckung mit dem Streckzentrum (0, 0) und
dem Streckfaktor 2:
gestrecktes Vieleck:
A'(60, 0), B'(140, 0), C'(140, 60), D'(60, 60)
Slide 56
56
Listenrepräsentation
Wir benötigen hier die Multiplikation eines Vektors mit einer Zahl.
Vieleck:
[[20, 0], [60, 0], [60, 30], [20, 30]]
Streckung mit dem Streckzentrum [0, 0] und
dem Streckfaktor 2:
2*[20, 0] [40, 0]
...
gestrecktes Vieleck:
[[40, 0], [120, 0], [120, 60], [40, 60]]
Slide 57
57
Aufgabe
Entwickeln Sie geeignete Funktionen zur Durchführung von Streckungen.
Slide 58
58
Lösungsvorschlag
def mul(k, v):
if len(v) == 0:
return []
else:
return [k * v[0]] + mul(k, v[1:])
def streckenPunktUrsprung(punkt, faktor):
return mul(faktor, punkt)
def streckenUrsprung(vieleck, faktor):
if len(vieleck) == 0:
return []
else:
return [streckenPunktUrsprung(vieleck[0], faktor)] +
streckenUrsprung(vieleck[1:], faktor)
def strecken(vieleck, zentrum, faktor):
return verschieben(
streckenUrsprung(
verschieben(vieleck, mul(-1, zentrum)),
faktor),
zentrum)
Slide 59
59
Schritt 3: Drehungen
Drehungen lassen sich durch
Multiplikation einer Matrix mit
einem Vektor realisieren.
Fall1: Drehzentrum im Ursprung
Drehung des Punktes P(x, y) um den
Winkel w:
cos(w) -sin(w)
x
*
sin(w)
cos(w)
x'
y
y'
Es gilt:
x' = cos(w)*x + (- sin(w))*y
y' = sin(w)*x + cos(w)*y
Fall 2: Drehzentrum beliebig
Verschiebe erst das Drehzentrum in den
Ursprung, führe die Drehung aus und
verschiebe wieder zurück an den
Ausgangspunkt
Slide 60
60
Listenrepräsentation
Wir repräsentieren Matrizen hier ebenfalls mit Hilfe von Listen.:
Vieleck:
[[20, 0], [60, 0], [60, 30], [20, 30]]
Matrix für eine Linksdrehung um das
Drehzentrum [0, 0] und dem Drehwinkel
90°: [[0, -1], [1, 0]]
[[0, -1], [1, 0]] * [20, 0] [0, 20]
...
gedrehtes Vieleck:
[[0, 20], [0, 60], [-30, 60], [-30, 20]]
Slide 61
61
Aufgabe
Entwickeln Sie geeignete Funktionen zur Durchführung von Drehungen. Bei
der Implementierung mit Python müssen Sie durch "import math" die
benötigten mathematischen Operationen bereitstellen. Ein Aufruf der cosFunktion lautet hier "math.cos(x)". Das Ergebnis wird im Bogenmaß
geliefert. Mit "math.radians(w)" können Sie einen Winkel im Gradmaß ins
Bogenmaß umrechen. Die umgekehrte Operation führt "math.degrees(x)"
aus. Weitere Informationen erhalten Sie in der mitgelieferten
Dokumentation.
Slide 62
62
Lösungsvorschlag
def skalprod(v, w):
if len(v) == 0:
return 0
else:
return (v[0] * w[0]) + skalprod(v[1:], w[1:])
def matrixmul(m, v):
if len(m) == 0:
return []
else:
return [skalprod(m[0], v)] + matrixmul(m[1:], v)
Slide 63
63
Lösungsvorschlag
def drehenPunktUrsprung(punkt, w):
import math
return matrixmul([
[math.cos(math.radians(w)), -math.sin(math.radians(w))],
[math.sin(math.radians(w)), math.cos(math.radians(w))]], punkt)
def drehenUrsprung(vieleck, w):
if len(vieleck) == 0:
return []
else:
return [drehenPunktUrsprung(vieleck[0], w)] +
drehenUrsprung(vieleck[1:], w)
def drehen(vieleck, zentrum, winkel):
return verschieben(
drehenUrsprung(
verschieben(vieleck, mul(-1, zentrum)),
winkel),
zentrum)
Slide 64
64
Weitere Ideen
Erweitern Sie das System um die Möglichkeit, Spiegelungen durchzuführen.
Slide 65
65
Teil 4
Miniprojekt: Automatensimulator
Slide 66
66
Miniprojekt "Automatensimulator"
Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines
beliebigen Automaten simulieren kann.
0
0
akzeptor
1
g
u
Ok!
1
00011011
Slide 67
Schritt 1: Automatenbeschreibung
67
Mit Hilfe endlicher Automaten kann man formale Sprachen erkennen. Der
dargestellte endliche Automat erkennt die Sprache der 0-1-Wörter mit
gerader Parität (gerader Anzahl von 1en). Es handelt sich um einen sog.
Akzeptor, der keine Ausgaben erzeugt.
0
0
1
g
u
1
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
Anfangszustand:
za = g
Endzustände:
zE = {g}
Überführungsfunktion:
:
:
:
:
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
Slide 68
Aufgabe
68
Der Automat lässt sich wie folgt mit
Funktionen modellieren. Implementieren Sie diese Funktionen.
0
0
1
Spezifikation:
g
anfangszustandP
'g'
Spezifikation:
endzustandP
'g'
z
True
Spezifikation:
deltaP
'g'
z
'0'
e
'g'
u
1
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
Anfangszustand:
za = g
Endzustände:
zE = {g}
Überführungsfunktion:
:
:
:
:
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
Slide 69
69
Aufgabe
Modellieren und implementieren Sie
noch einen weiteren Automaten
(ohne Ausgabe).
Slide 70
70
Aufgabe
Der unten abgebildete Automat zur Steuerung einer Ampel ist ein
Transduktor. In jedem Zustand wird bei jeder Eingabe eine Ausgabe
erzeugt. Z. B. wird im Zustand "rot" bei der Eingabe "t" (Tag) die Ausgabe
"OOo" (rot an, gelb an, grün aus) erzeugt. Beschreiben Sie diesen
Transduktor mit Hilfe von Funktionen.
Slide 71
71
Lösungsvorschlag
def anfangszustandP():
return 'g'
Zustandsmenge:
Z = {g, u}
Eingabemenge:
E = {0, 1}
def endzustandP(z):
if (z == 'g'):
return True
elif (z == 'u'):
return False
Anfangszustand:
za = g
Endzustände:
zE = {g}
def deltaP(z, e):
if (z == 'g') and (e == '0'):
return 'g'
elif (z == 'u') and (e == '0'):
return 'u'
elif (z == 'g') and (e == '1'):
return 'u'
elif (z == 'u') and (e == '1'):
return 'g'
Überführungsfunk.: :
:
:
:
0
(g,
(g,
(u,
(u,
0)
1)
0)
1)
g
u
u
g
0
1
g
u
1
Slide 72
72
Lösungsvorschlag
def anfangszustandA():
return 'ge';;
def deltaA(z, e):
if (z == 'ro') and (e == 't'):
return 'rg'
elif (z == 'rg') and (e == 't'):
return 'gr'
elif (z == 'gr') and (e == 't'):
return 'ge'
elif (z == 'ge') and (e == 't'):
return 'ro'
...
def lambdaA(z, e):
if (z == 'ro') and (e == 't'):
return 'OOo'
elif (z == 'rg') and (e == 't'):
return 'ooO'
elif (z == 'gr') and (e == 't'):
return 'oOo'
...
Slide 73
73
Schritt 2: Verarbeitung v. Eingabefolgen
Es soll ein Akzeptor-System entwickelt werden, mit dem eine Folge von
Eingaben verarbeiten werden kann und rückgemeldet wird, ob diese Folge
in einen Endzustand überführt.
0
0
1
Ok!
00011011
g
u
1
Slide 74
74
Aufgabe
Erweitern Sie das funktionale Programm
um die spezifizierten Funktionen.
Tipp: siehe nächste Folie
def anfangszustandP():
return 'g'
def endzustandP(z):
if (z == 'g'):
return True
elif (z == 'u'):
return False
def deltaP(z, e):
if (z == 'g') and (e == '0'):
return 'g'
elif (z == 'u') and (e == '0'):
return 'u'
elif (z == 'g') and (e == '1'):
return 'u'
elif (z == 'u') and (e == '1'):
return 'g'
0
0
1
g
u
1
Spezifikation:
simulatorP
'g'
['0', '1', '0']
z
eListe
'u'
Spezifikation:
akzeptorP
['0', '1', '0']
eListe
Fal
se
Slide 75
Aufgabe
75
Tipp:
Verallgemeinern Sie die unten an
konkreten Beispielen gezeigten
Problemreduktionen.
0
0
1
g
u
1
Fall 1: Verarbeite eine leere Eingabenliste
simulartorP('g', []) 'g'
Fall 2: Verarbeite eine nicht-leere Eingabenliste
simulatorP('g', ['1', '0', '0']) simulatorP('u', ['0', '0'])]
u
'u'
Slide 76
Aufgabe
76
Entwickeln Sie eine Funktion zur Realisierung eines Ampelsimulators.
Spezifikation:
simulatorA
'ro'
['t', 't', 't', 't']
z
eListe
[OOo, ooO, oOo, Ooo]
Slide 77
Lösungsvorschlag
77
Fall 1: Verarbeite eine leere Eingabenliste
simulartorP('g', []) 'g'
Fall 2: Verarbeite eine nicht-leere Eingabenliste
simulatorP('g', ['1', '0', '0']) simulatorP('u', ['0', '0'])]
u
'u'
def simulatorP(z, eListe):
if len(eListe) == 0:
return z
else:
return simulatorP(deltaP(z, eListe[0]), eListe[1:])
def akzeptorP(eListe):
return enzustand(simulatorP(anfangszustand(), eListe))
Slide 78
78
Weitere Ideen
Entwickeln Sie ein System, bei dem die Arbeitsweise eines Transduktors
(Automat mit Ausgabe) simuliert wird.
Slide 79
79
Schritt 3: Automat als Eingabe
Es soll ein universelles Akzeptor-System entwickelt werden, mit dem eine
Folge von Eingaben mit einem beliebig vorgegebenen Automaten
verarbeiten werden kann.
def anfangszustandP():
...
def endzustandP(z):
...
def deltaP(z, e):
...
Ok!
00011011
Slide 80
Aufgabe
80
Verallgemeinern Sie das bisher entwickelte Simulator-System. Benutzen Sie
die folgende Vereinbarung:
Eine Automatenbeschreibung ist ein Tripel (az, ez, de) mit:
- az ist eine Funktion zur Beschreibung des Anfangszustands.
- ez ist eine Funktion zur Beschreibung der Endzustände.
- de ist eine Überführungsfunktion, die jedem Zustand und jeder Eingabe
aus einen neuen Zustand zuordnet.
Spezifikation:
Funktionen
simulator
(anfangszustandP, enzustandP, deltaP)
'g'
['0', '1', '0']
aut
z
eListe
'u'
Slide 81
81
Lösungsvorschlag
def simulator(aut, z, eListe):
if len(eListe) == 0:
return z
else:
return simulator(aut, aut[2](z, eListe[0]), eListe[1:])
def akzeptor(aut, eListe):
return aut[1](simulator(aut, aut[0](), eListe))
Slide 82
82
Weitere Ideen
Entwickeln Sie ein System, bei dem die Arbeitsweise eines beliebigen
Transduktors simuliert wird.
Slide 83
83
Teil 5
Miniprojekt: Programminterpreter
Slide 84
84
Miniprojekt "Programminterpreter"
Ziel ist es, ein System zur Ausführung einfacher imperativer Programme zu
entwickeln.
{b: 2; u: 5}
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
{b: 256; u: 0; p: 32}
Interpreter
Slide 85
Problemvereinfachung
85
Ausgangszustand
Zuweisungsprogramm
Endzustand
{x: 2; y: 5}
BEGIN
z := x;
x := y;
y := z
END
Interpreter
{x: 5; y: 2; z: 5}
Wir betrachten zunächst nur Programme, die aus "primitiven Zuweisungen"
vom Typ := bestehen.
Slide 86
Funktionale Modellierung
86
Ausgangszustand
Zuweisungsprogramm
Endzustand
{x: 2; y: 5}
BEGIN
z := x;
x := y;
y := z
END
Interpreter
{x: 5; y: 2; z: 5}
Spezifikation:
PrimZuwSeqAusfuehren
[(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')]
[('x', 2), ('y', 5)]
zuweisungen
zustand
[('x', 5), ('y', 2), ('z', 5)]
Slide 87
87
Schritt 1: Variablenzustände
Variablenzustände beschreiben die jeweils aktuellen Variablenbelegungen.
Zur Verarbeitung einer primitiven Zuweisung wie z := x muss der Wert
einer Variablen (hier x) bzgl. des aktuellen Variablenzustands ermittelt
werden und der Wert einer Variablen (hier y) im Variablenzustand verändert
(oder neu angelegt) werden.
{x: 2; y: 5}
z := x;
{x: 2; y: 5; z: 2}
x := y;
{x: 5; y: 5; z: 2}
y := z
{x: 5; y: 2; z: 2}
Slide 88
88
Aufgabe
Modellieren Sie geeignete Funktionen, mit denen der Wert einer Variablen
bzgl. eines Variablenzustands ermittelt werden kann und der Wert einer
Variablen im Variablenzustand verändert (oder neu angelegt) werden kann.
Entwickeln Sie anschließend mit Hilfe rekursiver
Problemreduktionsschemata geeignete Funktionsdeklarationen.
Slide 89
Lösungsvorschlag
89
Spezifikation:
VariablenWert
'y'
[('x', 2), ('y', 5)]
bezeichner
zustand
5
Spezifikation:
NeuerZustand
'z'
2
[('x', 2), ('y', 5)]
bezeichner
wert
zustand
[('x', 2), ('y', 5), ('z', 2)]
NeuerZustand
'x'
5
[('x', 2), ('y', 5), ('z', 2)]
bezeichner
wert
zustand
[('x', 5), ('y', 5), ('z', 2)]
Slide 90
90
Lösungsvorschlag
def VariablenWert(bezeichner, zustand):
if len(zustand) == 0:
return '?'
else:
if bezeichner == zustand[0][0]:
return zustand[0][1]
else:
return VariablenWert(bezeichner, zustand[1:])
def NeuerZustand(bezeichner, wert, zustand):
if len(zustand) == 0:
return [(bezeichner, wert)]
else:
if bezeichner == zustand[0][0]:
return [(bezeichner, wert)] + zustand[1:]
else:
return [zustand[0]] +
NeuerZustand(bezeichner, wert, zustand[1:])
Slide 91
91
Schritt 2: Zuweisungsinterpreter
Mit Hilfe geeigneter Funktionen sollen jetzt einzelne primitive Zuweisungen
bzw. Sequenzen primitiver Zuweisungen ausgeführt werden.
{x: 2; y: 5}
z := x;
{x: 2; y: 5; z: 2}
x := y;
{x: 5; y: 5; z: 2}
y := z
{x: 5; y: 2; z: 2}
Slide 92
92
Aufgabe
Modellieren und implementieren Sie die hierzu erforderlichen Funktionen.
Slide 93
93
Lösungsvorschlag
Spezifikation:
PrimZuwAusfuehren
(':=', 'z', 'y')
[('x', 2), ('y', 5)]
zuweisung
zustand
[('x', 2), ('y', 5), ('z', 2)]
Spezifikation:
PrimZuwSeqAusfuehren
[(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')]
[('x', 2), ('y', 5)]
zuweisungen
zustand
[('x', 5), ('y', 2), ('z', 5)]
Slide 94
94
Lösungsvorschlag
def PrimZuwAusfuehren(zuweisung, zustand):
return NeuerZustand(zuweisung[1],
VariablenWert(zuweisung[2], zustand), zustand)
def PrimZuwSeqAusfuehren(zuweisungen, zustand):
if len(zuweisungen) == 0:
return zustand
else:
return PrimZuwSeqAusfuehren(zuweisungen[1:],
PrimZuwAusfuehren(zuweisungen[0], zustand))
Slide 95
95
Schritt 3: Terminterpreter
Auf der rechten Seite einer Zuweisung sollen jetzt auch beliebige
Rechenterme erlaubt sein. Wir beschränken uns auf das Rechnen mit
ganzen Zahlen. Als Rechenoperationen sollen daher +, -, *, / (Division ohne
Rest), % (Rest bei der Division) betrachtet werden.
{x: 2; y: 5}
x := x-y;
{x: -3; y: 5}
y := x+y;
{x: -3; y: 2}
x := y-x
{x: 5; y: 2}
Slide 96
96
Aufgabe
Ergänzen Sie die bereits begonnene Funktionsdeklaration und
verallgemeinern Sie den Zuweisungsinterpreter. Beachten Sie die hier
gewählte Darstellung von Termen, z. B.:
z+(x-y) wird dargestellt durch ('+', 'z', ('-', 'x', 'y'))
x+1 wird dargestellt durch ('+', 'x', 1)
def TermWert(term, zustand):
if isinstance(term, int):
return term
else:
if not isinstance(term, tuple):
return VariablenWert(term, zustand)
else:
if term[0] == '+':
return TermWert(term[1], zustand) +
TermWert(term[2], zustand)
else:
...
Slide 97
97
Schritt 4: Bedingungsinterpreter
Als nächstes sollen Anweisungen mit Bedingungen (wie z. B. if (x > 0) then
...) betrachtet werden. Hierzu muss der Interpreter Bedingungen auswerten
können. Der Einfachheit halber betrachten wir nur Bedingungen vom Typ
sollen also keine Rolle spielen.
Spezifikation:
BooleWert
('>', 'y', ('+', 'x', 'x'))
[('x', 2), ('y', 5)]
term
zustand
True
Slide 98
98
Aufgabe
Entwickeln Sie analog zur Auswertung von Rechentermen eine Funktion zur
Auswertung von Bedingungen.
Slide 99
99
Schritt 5: Kontrollinterpreter
Ziel ist es, Fallunterscheidungs- und Wiederholungsanweisungen
auszuführen, wie sie etwa im unten dargestellten Programm vorkommen.
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
Slide 100
100
Aufgabe
Überlegen Sie sich eine Darstellung von Kontrollanweisungen (wie im
Programm) mit den von Python zur Verfügung gestellten Datenstrukturen.
Entwickeln Sie geeignete Funktionen zur Ausführung dieser Anweisungen.
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
Slide 101
101
Lösungsvorschlag
Fallunterscheidung:
('if', (..Bedingung..), [..Then-Anweisungen..], [..Else-Anweisungen..])
Wiederholung:
('while', (..Bedingung..), [..Anweisungen..])
BEGIN
p := 1;
WHILE u > 0 DO
BEGIN
IF u mod 2 = 1 THEN
BEGIN
u := u – 1;
p := p * b;
END;
u := u div 2;
b := b* b;
END
END
[
(':=', 'p', 1),
('while', ('>', 'u', 0),
[
('if', ('==', ('%', 'u', 2), 1),
[
(':=', 'u', ('-', 'u', 1)),
(':=', 'p', ('*', 'p', 'b'))
],[]),
(':=', 'u', ('/', 'u', 2)),
(':=', 'b', ('*', 'b', 'b'))
])
]
Slide 102
102
Lösungsvorschlag
def AnwAusfuehren(anweisung, zustand):
if anweisung[0] == ':=':
return NeuerZustand(anweisung[1],
TermWert(anweisung[2], zustand), zustand)
else:
if anweisung[0] == 'if':
if BooleWert(anweisung[1], zustand):
return AnwSeqAusfuehren(anweisung[2], zustand)
else:
return AnwSeqAusfuehren(anweisung[3], zustand)
else:
if anweisung[0] == 'while':
if BooleWert(anweisung[1], zustand):
return AnwAusfuehren(anweisung,
AnwSeqAusfuehren(anweisung[2], zustand))
else:
return zustand
Slide 103
103
Lösungsvorschlag
def AnwSeqAusfuehren(anweisungen, zustand):
if len(anweisungen) == 0:
return zustand
else:
return AnwSeqAusfuehren(anweisungen[1:],
AnwAusfuehren(anweisungen[0], zustand))
Slide 104
104
Lösungsvorschlag
def ProgrammAusfuehren(programm, zustand):
return AnwSeqAusfuehren(programm, zustand)
Beachten Sie, dass ein Programm hier immer eine Anweisungssequenz ist,
auch wenn es nur aus einer Anweisung besteht.
Slide 105
105
Teil 6
Deklarative Programmierung
Slide 106
106
Ein Problem - zwei Lösungen
Problem:
Wie fügt man die Elemente einer Listen (die alle Zeichenketten sein sollen)
zu einer einzigen Zeichenkette zusammen?
Lösung:
Lösung:
Stell eine Hilfsliste "ergebnis" wie folgt
zusammen:
Starte mit der einer leeren Liste.
Füge Schritt für Schritt alle Elemente der
Liste jeweils am Ende der Hilfsliste ein.
Die am Schluss erhaltene Hilfsliste ist die
gesuchte Liste.
Wenn die Liste leer ist, dann ist die leere
Liste bereits das Ergebnis. Wenn die Liste
nicht leer ist, dann erhält man das Ergebnis
wie folgt: Man fügt das erste Element
vorne an die Zeichenkette, die man erhält,
wenn man alle Elemente der Restliste
zusammenfügt.
def zusammenfuegen(liste):
ergebnis = ''
for wort in liste:
ergebnis = ergebnis + wort
return ergebnis
def zusammenfuegen(liste):
if len(liste) == 0:
return ''
else:
return liste[0] + zusammenfuegen(liste[1:])
Imperativer Ansatz:
Man beschreibt Schritt für Schritt den
Vorgang, wie man alle Elemente der Liste
zusammenfügt.
Deklarativer Ansatz:
Man beschreibt, welche Eigenschaften das
Ergebnis haben soll, das man beim
Zusammenfügen erhält.
Slide 107
107
Imperative Programmierung
Imperative Programmierung besteht darin, eine (mehr oder weniger
abstrakte) Maschine mit Hilfe von Anweisungen zu steuern.
A.-Zustand
{liste: ['HA', 'LL', 'O']}
Anweisungen
def zusammenfuegen(liste):
ergebnis = ''
for wort in liste:
ergebnis = ergebnis + wort
return ergebnis
E.-Zustand
{ergebnis: 'HALLO']}
Registermaschine
Ansatz: Beschreiben, 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.
Slide 108
108
Deklarative Programmierung
Deklarative Programmierung besteht darin, den Problemkontext
(Miniwelt) mit gegebenen Mitteln (hier: Funktionen) zu beschreiben.
Term
zusammenfuegen(['HA', 'LL', 'O'])
def zusammenfuegen(liste):
if len(liste) == 0:
Deklarationen
return ''
else:
return liste[0] + zusammenfuegen(liste[1:])
Ergebnis
Reduktionsmaschine
'HALLO'
Ansatz: Beschreiben, was in der Modellwelt gelten soll
Die funktionale Programmierung arbeitet 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.
Slide 109
109
Fazit
Funktionale Programmierung erfolgt auf einem höheren Abstraktionsniveau:
- keine Anweisungen an eine Maschine,
- sondern Beschreibung funktionaler Zusammenhänge.
Konsequenzen:
- Funktionale Programme sind kurz.
- Funktionale Programme sind leicht zu verstehen.
- Funktionale Programmierung ist wenig fehleranfällig.
- Funktionale Programmierung eignet sich zum „Prototyping“.
Slide 110
110
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)
[Fischbacher 97] T. Fischbacher: Funktionale Programmierung. In: LOG IN 17 (1997) Heft 3 /
4, S. 24-26.
[ISB 97] Staatliches Institut für Schulpädagogik und Bildungsforschung München (Hrsg.):
Funktionales Programmieren in Gofer. Baustein zur Didaktik der Informatik. München, 1997.
[Puhlmann 98] H. Puhlmann: Funktionales Programmieren - Eine organische Verbindung von
Informatikunterricht und Mathematik. In: LOG IN 18 (1998) Heft 2, S. 46-50.
[Schwill 93] A. Schwill: Funktionale Programmierung mit Caml. In: LOG IN 13 (1993) Heft 4,
S. 20-30.
[Wagenknecht 94] Christian Wagenknecht: Rekursion. Ein didaktischer Zugang mit
Funktionen. Bonn: Dümmlers Verlag 1994.
[Wolff von Gudenberg 96] J. Wolff. von Gudenberg: Algorithmen, Datenstrukturen, Funktionale
Programmierung. Eine praktische Einführung mit Caml Light. Bonn: Addison-Wesley 1996.