Funktionale Programmierung

Slides:



Advertisements
Ähnliche Präsentationen
Algorithmen und Datenstrukturen
Advertisements

Anzahl der ausgefüllten und eingesandten Fragebögen: 211
Handelskalkulation Inhaltsverzeichnis
Vorlesung: 1 Betriebliche Informationssysteme 2003 Prof. Dr. G. Hellberg Studiengang Informatik FHDW Vorlesung: Betriebliche Informationssysteme Teil3.
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Telefonnummer.
1 JIM-Studie 2010 Jugend, Information, (Multi-)Media Landesanstalt für Kommunikation Baden-Württemberg (LFK) Landeszentrale für Medien und Kommunikation.
= = = = 47 = 47 = 48 = =
Statistiken und Tabellen
Java: Objektorientierte Programmierung
Sortierverfahren Richard Göbel.
WS Algorithmentheorie 02 - Polynomprodukt und Fast Fourier Transformation Prof. Dr. Th. Ottmann.
Dynamische Programmierung (2) Matrixkettenprodukt
WS Algorithmentheorie 08 – Dynamische Programmierung (2) Matrixkettenprodukt Prof. Dr. Th. Ottmann.
Rechneraufbau & Rechnerstrukturen, Folie 2.1 © W. Oberschelp, G. Vossen W. Oberschelp G. Vossen Kapitel 2.
Mh9S170Nr6 a. x1= –9; x2 = 1 b. x1= –4; x2 = 1 c. x1= 1; x2 = 2 d. leer e. x1= –15; x2 = 4,2 f. x1= –3,53; x2 = 1,28 g. leer h. x1= 0,2; x2 = 2 i. x1=
Internet facts 2008-II Graphiken zu dem Berichtsband AGOF e.V. September 2008.
Vorlesung: 1 Betriebliche Informationssysteme 2003 Prof. Dr. G. Hellberg Studiengang Informatik FHDW Vorlesung: Betriebliche Informationssysteme Teil2.
Vererbung Spezialisierung von Klassen in JAVA möglich durch
PKJ 2005/1 Stefan Dissmann Rückblick auf 2005 Was zuletzt in 2005 vorgestellt wurde: Klassen mit Attributen, Methoden und Konstruktoren Referenzen auf.
PKJ 2005/1 Stefan Dissmann Zusammenfassung Bisher im Kurs erarbeitete Konzepte(1): Umgang mit einfachen Datentypen Umgang mit Feldern Umgang mit Referenzen.
Zusammenfassung Vorwoche
PKJ 2005/1 Stefan Dissmann Zusammenfassung Vorwoche Methoden sind mit einem Namen versehene Programmabschnitte besitzen Rückgabetyp, Namen, Parameterliste.
Christian Schindelhauer
Christian Schindelhauer
Christian Schindelhauer
Differentielles Paar UIN rds gm UIN
Prof. Dr. Bernhard Wasmayr
Studienverlauf im Ausländerstudium
Dieter Bergmann, Lichtenfels
Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Prof. Dr. Bernhard Wasmayr VWL 2. Semester
AWA 2007 Natur und Umwelt Natürlich Leben
Rechneraufbau & Rechnerstrukturen, Folie 12.1 © W. Oberschelp, G. Vossen W. Oberschelp G. Vossen Kapitel 12.
Prof. Dr. Günter Gerhardinger Soziale Arbeit mit Einzelnen und Familien Übersicht über die Lehrveranstaltung Grundlegende Bestimmungsfaktoren der Praxis.
20:00.
Zusatzfolien zu B-Bäumen
LS 2 / Informatik Datenstrukturen, Algorithmen und Programmierung 2 (DAP2)
Eine Einführung in die CD-ROM
Dokumentation der Umfrage
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
Wir üben die Malsätzchen
Syntaxanalyse Bottom-Up und LR(0)
Dualzahlen und ihre logischen Verknüpfungen
Polynome und schnelle Fourier-Transformation
Funktionale Programmierung mit Caml
Das RSA-Verfahren - Einsatz von Standardalgorithmen in der Kryptologie
Addieren und Subtrahieren von Dezimalzahlen
Der Ablauf eines Clear Rex Klärzyklus
Ertragsteuern, 5. Auflage Christiana Djanani, Gernot Brähler, Christian Lösel, Andreas Krenzin © UVK Verlagsgesellschaft mbH, Konstanz und München 2012.
Geometrische Aufgaben
Eine lllustration der Herausforderungen des Stromsystems der Zukunft
Symmetrische Blockchiffren DES – der Data Encryption Standard
Retuschen.ppt Die folgende Schau zeigt die Möglichkeiten, mit PhotoDraw Digitalbilder zu retuschieren. Vergleichen Sie jeweils zwei Bildpaare durch fleissiges.
Zahlentheorie und Zahlenspiele Hartmut Menzer, Ingo Althöfer ISBN: © 2014 Oldenbourg Wissenschaftsverlag GmbH Abbildungsübersicht / List.
MINDREADER Ein magisch - interaktives Erlebnis mit ENZO PAOLO
1 (C)2006, Hermann Knoll, HTW Chur, FHO Quadratische Reste Definitionen: Quadratischer Rest Quadratwurzel Anwendungen.
Parkplatz-Orga Diese Version ist vom finale Version!
PHP: Operatoren und Kontrollstrukturen
Schutzvermerk nach DIN 34 beachten 20/05/14 Seite 1 Grundlagen XSoft Lösung :Logische Grundschaltung IEC-Grundlagen und logische Verknüpfungen.
Folie Beispiel für eine Einzelauswertung der Gemeindedaten (fiktive Daten)
Unternehmensbewertung Thomas Hering ISBN: © 2014 Oldenbourg Wissenschaftsverlag GmbH Abbildungsübersicht / List of Figures Tabellenübersicht.
Forschungsprojekt Statistik 2013 „Jugend zählt“ – Folie 1 Statistik 2013 „Jugend zählt“: Daten zur Arbeit mit Kindern und Jugendlichen.
Folie Einzelauswertung der Gemeindedaten
Berechenbarkeit Klaus Becker Berechenbarkeit.
Datum:17. Dezember 2014 Thema:IFRS Update zum Jahresende – die Neuerungen im Überblick Referent:Eberhard Grötzner, EMA ® Anlass:12. Arbeitskreis Internationale.
1 Medienpädagogischer Forschungsverbund Südwest KIM-Studie 2014 Landesanstalt für Kommunikation Baden-Württemberg (LFK) Landeszentrale für Medien und Kommunikation.
Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar?
 Präsentation transkript:

Funktionale Programmierung Klaus Becker 2007

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

Von der Registermaschine zur funktionalen Abstraktion Teil 1 Von der Registermaschine zur funktionalen Abstraktion

Registermaschine Eine Registermaschine bearbeitet beliebig eingebbare Daten nach einem fest vorgegebenen Programm. Daten Programm 1: 5 2: 3 3: 0 4: 0 5: 0 .. > 1 JMP 4 2 INC 1 3 DEC 2 4 TST 2 5 JMP 2 6 HLT > 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. Wenn Register i ungleich 0 ist, dann gehe zu Zeile x+1, sonst zu Zeile x+2. > x TST i > x HLT Beende die Bearbeitung.

Aufgabe 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

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

Spaghetti-Code mit Sprunganweisungen Durch die vielen Sprunganweisungen verliert man leicht den Überblick über die Ablauflogik. 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. 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 "Spaghetti-Code"

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

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 def minimum(a, b): m = 0 while (a <> 0) and (b <> 0): m = m + 1 a = a - 1 b = b - 1 return m >>> m = 0 >>> minimum(5, 7) >>> m 5 >>> minimum(5, 7) 5

Unterprogramm mit Seiteneffekt 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 def minimum(a, b): m = 0 while (a <> 0) and (b <> 0): m = m + 1 a = a - 1 b = b - 1 return m >>> m = 3 >>> minimum(5, 7) >>> m 8 >>> m = 0 >>> minimum(5, 7) >>> m 5 >>> minimum(5, 7) 5 Unterprogramm mit Seiteneffekt

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

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.

Konzepte der funktionalen Programmierung Teil 2 Konzepte der funktionalen Programmierung

Fallstudie: Chiffrieren Ziel ist es, Chiffrierverfahren (die auf modularem Rechnen basieren), mit Hilfe funktionaler Programme zu beschreiben. PYLZFOWBNQCYBUVNCBLGYCHYAYBYCGMWBLCZNYHNTCZYLN 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 Schlüssel: D Quelltext: Geheimtext: SALVECAESAR VDOYHFDHVDU

Additives Chiffrierverfahren Codierung: Code: A → 1 Blocklänge: 2 AA → 0101 AB → 0102 ... ZZ → 2626 AS#TE#RI#X 0119#2005#1809#24 Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Entschlüsselung: privater Schlüssel (e, m) = (898, 3000) z → (z + e) % m 2221#1107#1010#2126 Bed.: (d + e) % m = 0 0119#2005#1809#24 Decodierung: Code: A → 1 Blocklänge: 2 AA → 0101 AB → 0102 ... ZZ → 2626 0119#2005#1809#24 AS#TE#RI#X

Additives Chiffrierverfahren Wir betrachten zunächst das Verschlüsseln und Entschlüsseln von Zahlenfolgen mit Schlüsseln, die aus zwei Komponenten bestehen. Da Ver- und Entschlüsseln gleich funktionieren, reicht es, nur das Verschlüsseln zu betrachten. Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: m ist größer als die maximale Codezahl 2221#1107#1010#2126 Entschlüsselung: privater Schlüssel (e, m) = (898, 3000) z → (z + e) % m 2221#1107#1010#2126 Bed.: d + e = m 0119#2005#1809#24

Funktionale Modellierung Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Spezifikation: verschluesselnZahl 119 zahl 2221 (2102, 3000) schluessel Eingaben: zahl: die zu verschlüsselnde Zahl schluessel: Zahlenpaar bestehend aus der zu addierenden Konstante und dem Divisionsmodul Ausgabe: die berechnete verschlüsselte Zahl

Funktionale Modellierung Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Spezifikation: verschluesseln [119, 2005, 1809, 24] zahlenListe [2221, 1107, 1010, 2126] (2102, 3000) schluessel 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

Funktionales Programm Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Spezifikation: verschluesselnZahl 119 zahl 2221 (2102, 3000) schluessel Implementierung: def verschluesselnZahl(zahl, schluessel): return (zahl + schluessel[0]) % schluessel[1]

Funktionales Programm Verschlüsselung: öffentlicher Schlüssel (d, m) = (2102, 3000) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Spezifikation: verschluesseln [119, 2005, 1809, 24] zahlenListe [2221, 1107, 1010, 2126] (2102, 3000) schluessel Implementierung: def verschluesseln(zahlenListe, schluessel): if len(zahlenListe) == 0: return [] else: return [verschluesselnZahl(zahlenListe[0], schluessel)] + verschluesseln(zahlenListe[1:], schluessel)

Datenstruktur "Tupel" Tupel sind unveränderbare Sequenzen. Man verwendet Tupel, wenn man ein Objekt repräsentieren möchte, das aus mehreren Komponenten besteht. Spezifikation: verschluesselnZahl Tupel 119 zahl 2221 (2102, 3000) schluessel Implementierung: def verschluesselnZahl(zahl, schluessel): return (zahl + schluessel[0]) % schluessel[1] Zugriff auf die Komponenten Zugriff auf die Komponenten

Datenstruktur "Liste" Listen sind veränderbare Sequenzen. Sie können (in Python) Objekte beliebigen Typs enthalten. Mit Listen kann man komplexere Strukturen modellieren. Spezifikation: Liste Liste verschluesseln [119, 2005, 1809, 24] zahlenListe [2221, 1107, 1010, 2126] (2102, 3000) schluessel Implementierung: Listenoperationen def verschluesseln(zahlenListe, schluessel): if len(zahlenListe) == 0: return [] else: return [verschluesselnZahl(zahlenListe[0], schluessel)] + verschluesseln(zahlenListe[1:], schluessel)

Listenoperationen Das Python-Protokoll zeigt die Listenoperationen, die im Folgenden benötigt werden. Weitere Listenoperationen findet man in der Dokumentation. >>> zahlenListe = [] >>> zahlenListe [] >>> len(zahlenListe) >>> zahlenListe = [119, 2005, 1809, 24] [119, 2005, 1809, 24] >>> zahlenListe[0] 119 >>> zahlenListe[1:] [2005, 1809, 24] >>> [zahlenListe[0]] + zahlenListe[1:] >>> ... leere Liste Länge einer Liste Zugriff auf Listenelemente Zugriff auf eine Restliste Konkatenation von Listen

Aufgabe Testen Sie die im funktionalen Programm vorkommenden Listenoperationen. Variieren Sie dabei auch die vorkommenden Parameterwerte. >>> zahlenListe = [] >>> zahlenListe [] >>> len(zahlenListe) >>> zahlenListe = [119, 2005, 1809, 24] [119, 2005, 1809, 24] >>> zahlenListe[0] 119 >>> zahlenListe[1:] [2005, 1809, 24] >>> [zahlenListe[0]] + zahlenListe[1:] >>> ...

Rekursive Problemreduktion 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)) → [verschluesselnZahl(119, (2102, 3000))] + verschluesseln([2005, 1809, 24], (2102, 3000)) [2221, 1107, 1010, 2126] Rekursionsschritt: Löse ein entsprechendes Problem [2221] [1107, 1010, 2126]

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)

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)) ...

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]

Funktionskomposition Kontrollstrukturen 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

Aufgabe Zur Übung rekursiver Funktionsdeklarationen sollen folgende Funktionen implementiert werden. Gehen Sie analog zur Funktion "verschluesseln" vor. Spezifikation: addieren [6, 13, 0, 5] zahlenListe [13, 20, 7, 12] 7 konstante Spezifikation: nullErsetzen [6, 0, 13, 0, 0, 5, 3] zahlenListe [6, 7, 13, 7, 7, 5, 3] 7 konstante

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) z → (z + d) % m 0119#2005#1809#24 Bed.: z < m m > maxCode 2221#1107#1010#2126 Entschlüsselung: privater Schlüssel (e, m) = (898, 3000) z → (z + e) % m 2221#1107#1010#2126 Bed.: (d + e) % m = 0 0119#2005#1809#24

Aufgabe Ändern Sie die entsprechenden Funktionsdeklarationen so ab, dass man das multiplikative Chiffrierverfahren erhält. Verschlüsselung: öffentlicher Schlüssel (d, m) = (7, 30) z → (z * d) % m 01#19#20#05#18#09#24 Bed.: z < m 07#13#20#05#06#03#18 Entschlüsselung: privater Schlüssel (e, m) = (13, 30) z → (z * e) % m 07#13#20#05#06#03#18 Bed.: (d * e) % m = 1 01#19#20#05#18#09#24

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)

Funktionen als Eingabeobjekte Codeduplizierung lässt sich vermeiden, wenn man Funktionen als Eingabeobjekte von Funktionen zulässt. Spezifikation: Funktion verschluesseln z → (z + d) % m verfahren [119, 2005, 1809, 24] zahlenListe [2221, 1107, 1010, 2126] (2102, 3000) schluessel Liste Tupel

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)

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.

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

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 [] else: return [verschluesselnZahl(zahlenListe[0], schluessel)] + verschluesseln(zahlenListe[1:], schluessel) Rekursion Fallunterscheidung

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: return [] else: return [verschluesselnZahl(verfahren, zahlenListe[0], schluessel)] + verschluesseln(verfahren, zahlenListe[1:], schluessel) Liste

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.

Miniprojekt "Geometrische Abbildungen" Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische Operationen durchführen kann.

Miniprojekt "Automatensimulator" Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines beliebigen Automaten simulieren kann. akzeptor 1 g u Ok! 1 0 0 0 1 1 0 1 1

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 Interpreter {b: 256; u: 0; p: 32}

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.

Miniprojekt: Geometrische Abbildungen Teil 3 Miniprojekt: Geometrische Abbildungen

Geometrische Abbildungen Ziel ist es, ein System zu entwickeln, mit dem man einfache geometrische Operationen durchführen kann.

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: 10 v = 10 verschobenes Vieleck: A'(10, 10), B'(50, 10), C'(50, 40), D'(10, 40)

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]]

Aufgabe 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 [70, 30, 70] [10, 0, 30] w

Aufgabe Realisieren Sie jetzt die Abbildung "Verschieben" mit Hilfe der bereits implementierten Vektoraddition. Spezifikation: verschiebenPunkt [40, 30] punkt [50, 30] [10, 10] vektor Spezifikation: verschieben [[20, 10], [40, 30], [70, 0]] vieleck [[30, 20], [50, 40], [80, 10]] [10, 10] vektor

Lösungsvorschlag Spezifikation: Implementierung: add [60, 30, 40] v [70, 30, 70] [10, 0, 30] w 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:])

Lösungsvorschlag Spezifikation: Spezifikation: verschiebenPunkt [40, 30] punkt [50, 30] [10, 10] vektor Spezifikation: verschieben [[20, 10], [40, 30], [70, 0]] vieleck [[30, 20], [50, 40], [80, 10]] [10, 10] vektor 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)

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.

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)

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)

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]]

Aufgabe Entwickeln Sie geeignete Funktionen zur Durchführung von Streckungen.

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)

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 x' *  sin(w) cos(w) 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

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]]

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 cos-Funktion 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.

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)

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)

Weitere Ideen Erweitern Sie das System um die Möglichkeit, Spiegelungen durchzuführen.

Miniprojekt: Automatensimulator Teil 4 Miniprojekt: Automatensimulator

Miniprojekt "Automatensimulator" Ziel ist es, ein System zu entwickeln, mit dem man das Verhalten eines beliebigen Automaten simulieren kann. akzeptor 1 g u Ok! 1 0 0 0 1 1 0 1 1

Schritt 1: Automatenbeschreibung 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. 1 g u Zustandsmenge: Z = {g, u} Eingabemenge: E = {0, 1} Anfangszustand: za = g Endzustände: zE = {g} Überführungsfunktion: : (g, 0)  g : (g, 1)  u : (u, 0)  u : (u, 1)  g

Aufgabe Der Automat lässt sich wie folgt mit Funktionen modellieren. Implemen-tieren Sie diese Funktionen. 1 g u Spezifikation: anfangszustandP 'g' Spezifikation: Zustandsmenge: Z = {g, u} Eingabemenge: E = {0, 1} Anfangszustand: za = g Endzustände: zE = {g} Überführungsfunktion: : (g, 0)  g : (g, 1)  u : (u, 0)  u : (u, 1)  g endzustandP 'g' z True Spezifikation: deltaP 'g' z 'g' '0' e

Aufgabe Modellieren und implementieren Sie noch einen weiteren Automaten (ohne Ausgabe).

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.

Lösungsvorschlag 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' Zustandsmenge: Z = {g, u} Eingabemenge: E = {0, 1} Anfangszustand: za = g Endzustände: zE = {g} Überführungsfunk.: : (g, 0)  g : (g, 1)  u : (u, 0)  u : (u, 1)  g 1 g u

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' ...

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. 1 g u 0 0 0 1 1 0 1 1 Ok!

Aufgabe Erweitern Sie das funktionale Programm um die spezifizierten Funktionen. Tipp: siehe nächste Folie 1 g u 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' Spezifikation: simulatorP 'g' z 'u' ['0', '1', '0'] eListe Spezifikation: akzeptorP False ['0', '1', '0'] eListe

Aufgabe Tipp: Verallgemeinern Sie die unten an konkreten Beispielen gezeigten Problemreduktionen. 1 g u 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'

Aufgabe Entwickeln Sie eine Funktion zur Realisierung eines Ampelsimulators. Spezifikation: simulatorA 'ro' z [OOo, ooO, oOo, Ooo] ['t', 't', 't', 't'] eListe

Lösungsvorschlag 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))

Weitere Ideen Entwickeln Sie ein System, bei dem die Arbeitsweise eines Transduktors (Automat mit Ausgabe) simuliert wird.

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! 0 0 0 1 1 0 1 1

Aufgabe 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) aut 'g' z 'u' ['0', '1', '0'] eListe

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))

Weitere Ideen Entwickeln Sie ein System, bei dem die Arbeitsweise eines beliebigen Transduktors simuliert wird.

Miniprojekt: Programminterpreter Teil 5 Miniprojekt: Programminterpreter

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 Interpreter {b: 256; u: 0; p: 32}

Problemvereinfachung Ausgangs-zustand {x: 2; y: 5} BEGIN z := x; x := y; y := z END Zuweisungs-programm Interpreter End- zustand {x: 5; y: 2; z: 5} Wir betrachten zunächst nur Programme, die aus "primitiven Zuweisungen" vom Typ <Var> := <Var> bestehen.

Funktionale Modellierung Ausgangs-zustand {x: 2; y: 5} BEGIN z := x; x := y; y := z END Zuweisungs-programm Interpreter End- zustand {x: 5; y: 2; z: 5} Spezifikation: PrimZuwSeqAusfuehren [(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')] zuweisungen [('x', 5), ('y', 2), ('z', 5)] [('x', 2), ('y', 5)] zustand

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}

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.

Lösungsvorschlag Spezifikation: Spezifikation: VariablenWert 'y' bezeichner 5 [('x', 2), ('y', 5)] zustand Spezifikation: NeuerZustand 'z' bezeichner [('x', 2), ('y', 5), ('z', 2)] 2 wert [('x', 2), ('y', 5)] zustand NeuerZustand 'x' bezeichner [('x', 5), ('y', 5), ('z', 2)] 5 wert [('x', 2), ('y', 5), ('z', 2)] zustand

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:])

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}

Aufgabe Modellieren und implementieren Sie die hierzu erforderlichen Funktionen.

PrimZuwSeqAusfuehren Lösungsvorschlag Spezifikation: PrimZuwAusfuehren (':=', 'z', 'y') zuweisung [('x', 2), ('y', 5), ('z', 2)] [('x', 2), ('y', 5)] zustand Spezifikation: PrimZuwSeqAusfuehren [(':=', 'z', 'x'), (':=', 'x', 'y'), (':=', 'y', 'z')] zuweisungen [('x', 5), ('y', 2), ('z', 5)] [('x', 2), ('y', 5)] zustand

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))

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}

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: ...

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 <Term> <Vergleich> <Term>. Logische Verknüpfungen von Bedingungen sollen also keine Rolle spielen. Spezifikation: BooleWert ('>', 'y', ('+', 'x', 'x')) term True [('x', 2), ('y', 5)] zustand

Aufgabe Entwickeln Sie analog zur Auswertung von Rechentermen eine Funktion zur Auswertung von Bedingungen.

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

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

Lösungsvorschlag Fallunterscheidung: ('if', (..Bedingung..), [..Then-Anweisungen..], [..Else-Anweisungen..]) Wiederholung: ('while', (..Bedingung..), [..Anweisungen..]) [ (':=', 'p', 1), ('while', ('>', 'u', 0), [ ('if', ('==', ('%', 'u', 2), 1), [ (':=', 'u', ('-', 'u', 1)), (':=', 'p', ('*', 'p', 'b')) ],[]), (':=', 'u', ('/', 'u', 2)), (':=', 'b', ('*', 'b', 'b')) ]) ] 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

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

Lösungsvorschlag def AnwSeqAusfuehren(anweisungen, zustand): if len(anweisungen) == 0: return zustand else: return AnwSeqAusfuehren(anweisungen[1:], AnwAusfuehren(anweisungen[0], zustand))

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.

Deklarative Programmierung Teil 6 Deklarative Programmierung

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: 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. def zusammenfuegen(liste): ergebnis = '' for wort in liste: ergebnis = ergebnis + wort return ergebnis Lösung: 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): 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.

Imperative Programmierung Imperative Programmierung besteht darin, eine (mehr oder weniger abstrakte) Maschine mit Hilfe von Anweisungen zu steuern. A.-Zustand {liste: ['HA', 'LL', 'O']} def zusammenfuegen(liste): ergebnis = '' for wort in liste: ergebnis = ergebnis + wort return ergebnis Register-maschine Anweisungen E.-Zustand {ergebnis: 'HALLO']} 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.

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: return '' else: return liste[0] + zusammenfuegen(liste[1:]) Reduktions- maschine Deklarationen Ergebnis '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.

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“.

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.bildung- rp.de/fileadmin/user_upload/informatik.bildung-rp.de/Weiterbildung/pps/WB- FunktionaleProgrammierungCaml.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.