Funktionale Programmierung

Slides:



Advertisements
Ähnliche Präsentationen
Algorithmen und Datenstrukturen
Advertisements

Algorithmentheorie 08 – Dynamische Programmierung (1)
Forschungszentrum caesar
Sortierverfahren Richard Göbel.
Formale Sprachen – Mächtigkeit von Maschinenmodellen
Übung 6.1Turing-Maschine 1.Machen Sie sich mit der Funktionsweise des Busy Beaver-Programms vertraut Vollziehen sie die 11 Schritte der ersten Turing-Tabelle.
Algorithmus. Ein Kochrezept, zum Beispiel: Kartoffelbrei.
Strukturen. In einer Struktur kann eine beliebige Anzahl von Komponenten (Daten) mit unterschiedlichen Datentypen (im Gegensatz zu Feldern) zusammengefaßt.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Tobias Lauer.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Perl-Grundlagen Teile der Präsentation von A. Grupp,
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Kapitel 7 Claudio Moraga, Gisbert Dittrich FBI Unido
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
EINI-I Einführung in die Informatik für Naturwissenschaftler und Ingenieure I Vorlesung 2 SWS WS 99/00 Gisbert Dittrich FBI Unido
Zusammenfassung Vorwoche
Addierwerke.
Rekursion Richard Göbel.
Kakuro Regeln und Strategien
Java programmieren mit JavaKara
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
BIT – Schaßan – WS 02/03 Basisinformationstechnologie HK-Medien Teil 1, 11.Sitzung WS 02/03.
Effiziente Algorithmen
Beweissysteme Hartmut Klauck Universität Frankfurt WS 06/
Funktionale Programmierung mit Caml
Vom Umgang mit Daten. public void myProgram() { int[] saeulenWerte = new int[world.getSizeX()]; for (int i = 0; i < saeulenWerte.length; i++) { saeulenWerte[i]
Institut für Wirtschaftsinformatik – Software Engineering, JKU Linz 1 Algorithmen und Datenstrukturen SS 2005 Mag.Th. Hilpold u. Dr. A.Stritzinger Institut.
2.4 Rekursion Klassifikation und Beispiele
C-Einstieg. Agenda 1Vorbereitung 2Aufbau eines Programms 2.1Header 2.2 Methoden 2.3Main 3Datentypen & Variablen 4Operatoren(+, -, *, /) 5Logik 5.1IF 5.2Switch.
Algorithmen und Datenstrukturen Übungsmodul 8
Objectives Verstehen was unterDelegate verstanden wird
PHP: Operatoren und Kontrollstrukturen
SFZ FN Sj. 13/14 Python 3 Rekursion Inf K1/2 Sj 13/14
Funktionale Programmierung
Mehrfachausführungen Schleifen in VB 2010 ee. Programmidee: Der Anwender gibt eine Zahl ein, und das Programm gibt die Mehrfachen dieser Zahl aus (das.
Berechenbarkeit Klaus Becker Berechenbarkeit.
Wann ist eine Funktion (über den natürlichen Zahlen) berechenbar?
Pool Informatik, Sj 11/12 GZG FN W.Seyboldt 1 Pool Informatik 5 GZG FN Sj. 11/12 Kopieren, Daten, Programme.
Funktionen. Aufgabe : Eingabe zweier Zahlen ---> Minimum bestimmen Dann nochmals Eingabe zweier Zahlen ---> Minimum bestimmen.
Sortierverfahren Mit VB 2010 express edition JBS Tr, info Q1.
Pointer. Grundsätzliches: Im Arbeitsspeicher werden Daten gespeichert. Um auf die Daten eindeutig zugreifen zu können, werden diesen Daten Adressen zugeordnet.
Funktionen, Felder und Parameter- übergabe. Funktionsaufruf mit Feld als Parameter: Parameter = Name des Feldes.
Tutorium Software-Engineering SS14 Florian Manghofer.
Wiederholte Programmausführung
Praktische Informatik 1
Das Entwurfsmuster Model-View-Controller
Konstruktoren.
Klausur „Diskrete Mathematik II“
2.4 Rekursion Klassifikation und Beispiele
Gliederung 0. Motivation und Einordnung 1. Endliche Automaten
Datentypen: integer, char, string, boolean
Datentypen: integer, char, string, boolean
Einführung in die Programmierung
Grundlagen und Grundbegriffe
Hexadezimale Darstellung von Zahlen
Unterschiedliche Kontrollstrukturen
Coden mit Python Was sind deine Erfahrungen mit Programmieren?
SS 04 Christiane Rauh Christian Hellinger
Es gibt Klassen, die mit der Entwicklungsumgebung ausgeliefert werden
Programmierung eines Computers (Funktionsweise)
Einführung in die Programmierung
1. Die rekursive Datenstruktur Liste 1.3 Rekursive Funktionen
1. Die rekursive Datenstruktur Liste 1.5 Das Entwurfsmuster Kompositum
REKURSION + ITERATION.
10 Schritte Video-Optin-Formel
Implementieren von Klassen
3. Die Datenstruktur Graph 3.2 Repräsentation von Graphen
Datentyp- umwandlung.
Vorstellen und Herleiten der Horner Schemas
E-Aufgaben in Stud.IP mit ViPS – erste Schritte –
 Präsentation transkript:

Funktionale Programmierung Klaus Becker 2018

Funktionale Programmierung

Warum funktional programmieren? Teil 1 Warum funktional programmieren?

Ausgangspunkt – die Hardware Bei der Entwicklung von Programmiersprachen stand zu Beginn die Steuerung der realen Hardware im Mittelpunkt. Mit einem überschaubaren Befehlssatz konnte man direkt die benutzte „Hardware-Maschine“ programmieren. org 100h start: mov dx,meldung1 mov ah,9h int 021h mov ah, 01h ;Wert über die Tastatur einlesen cmp al, '5' jne ende mov dx,meldung2 ende: mov ah,4Ch int 21h section .data meldung1: db 'Bitte geben Sie eine Zahl ein:', 13, 10, '$' meldung2: db 13, 10, 'Sie haben die Zahl 5 eingegeben.', 13, 10, '$' Wir orientieren uns im Folgenden nicht an realer Hardware, sonderrn benutzen mit der Registermaschine ein einfaches Maschinenmodell.

Registermaschine Registermaschinen enthalten einen Speicher, der aus einzelnen, durchnummerierten Registern besteht, in denen natürliche Zahlen gespeichert werden können. 5 > 0 TST 1 1 JMP 3 2 JMP 6 3 DEC 1 4 INC 0 5 JMP 0 6 HLT 1 3 2 ... ... Zur Steuerung der Registermaschinen gibt es eine einfache maschinennahe Programmiersprache, die nur die folgenden 5 Befehle kennt: > x INC i Erhöhe Register i um 1. Gehe zu Zeile x+1. > x DEC i Erniedrige Register i um 1. Gehe zu Zeile x+1. > x JMP i Gehe zu Zeile i. 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.

Programmanalyse Aufgabe (a) Kannst du auf den ersten Blick erkennen, was das gezeigte Registermaschinenprogramm leistet? (b) Führe das Programm aus. Gehe von der folgenden Anfangsbelegung der Register aus: {R0: 0; R1: 4; R2:6; R3:0; ...}. Weißt du jetzt, was das Programm leistet? Wenn nicht, dann führe weitere Tests durch. (c) Warum ist es so schwer zu erkennen, was das Programm leistet? 0 TST 1 1 JMP 3 2 JMP 6 3 TST 2 4 JMP 12 5 JMP 9 6 TST 2 7 JMP 18 8 HLT 9 TST 1 10 JMP 16 11 HLT 12 DEC 1 13 DEC 2 14 INC 0 15 JMP 0 16 DEC 1 17 JMP 9 18 DEC 2 19 JMP 6

Ablaufmodellierung mit Sprungbefehlen Spaghetti-Code Maschinennahe Programmiersprachen nutzen Sprungbefehle (wie z.B. JMP), um Wiederholungen zu realisieren. Sprungbefehle können leicht dazu führen, dass Programme völlig undurchschaubar werden. Im gezeigten Programm führen die Sprungbefehle zu einer Art Spaghetti-Code, bei dem die Ablauflogik nicht leicht zu entwirren ist. 0 TST 1 1 JMP 3 2 JMP 6 3 TST 2 4 JMP 12 5 JMP 9 6 TST 2 7 JMP 18 8 HLT 9 TST 1 10 JMP 16 11 HLT 12 DEC 1 13 DEC 2 14 INC 0 15 JMP 0 16 DEC 1 17 JMP 9 18 DEC 2 19 JMP 6 0 TST 1 1 JMP 3 2 JMP 6 3 TST 2 4 JMP 12 5 JMP 9 6 TST 2 7 JMP 18 8 HLT 9 TST 1 10 JMP 16 11 HLT 12 DEC 1 13 DEC 2 14 INC 0 15 JMP 0 16 DEC 1 17 JMP 9 18 DEC 2 19 JMP 6 Ablaufmodellierung mit Sprungbefehlen Spaghetti-Code

Verzicht auf Sprungbefehle Schon früh erkannte man die Schwäche von Sprungbefehlen und versucht seither, auf höheren Programmierebenen möglichst auf die Verwendung solcher Befehle zu verzichten. Strukturierte Programmierung benutzt keine Sprungbefehle zur Ablaufmodellierung, sondern Kontrollstrukturen, die bestimmte Ablaufmuster vorgeben. while (R1 != 0) and (R2 != 0): R0 = R0 + 1 R1 = R1 - 1 R2 = R2 - 1 while R1 != 0: while R2 != 0: Ablaufmodellierung mit Kontrollstrukturen Im Programm wird die Kontrollstruktur "Wiederholung mit Eintrittsbedingung" (dargestellt durch eine while-Schleife) zur Modellierung benutzt. Kontrollstrukturen erhöhen die Transparenz der Ablauflogik und verringern die Fehlerquote bei der Entwicklung von Programmen.

Prozeduren Als Weiterentwicklung von hardwareorientierten Programiersprachen können die prozeduralen Programmiersprachen angesehen werden. Hier können Sequenzen von Anweisungen zu Prozeduren zusammengefasst werden, die dann als Befehle für eine abstraktere, hypothetische Maschine gedeutet werden können. program bsp ! implicit none real :: x, y, z write( *, * ) 'x -> ' read( *, * ) x call sub( x, y, z ) write( *, * ) 'y -> ', y, 'z -> ', z end subroutine sub( a, b, c ) real :: a, b, c b = a**2 c = a**2 + b end subroutine sub

Programmanalyse Aufgabe (a) Was leistet die Prozedur minimum? (b) Ergänze den Python-Dialog. (c) Was ist an der gezeigten Vorgehensweise problematisch? def minimum(a, b): global m while (a != 0) and (b != 0): m = m + 1 a = a - 1 b = b - 1 >>> m = 0 >>> minimum(5, 8) >>> m >>> minimum(7, 2) >>> m

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 >>> m = 0 >>> minimum(5, 8) >>> m 5 >>> minimum(7, 2) >>> m 7 Unterprogramm mit Seiteneffekt Seiteneffekte führen dazu, dass das Verhalten von Funktionen schwer zu durchschauen ist. Insbesondere bei komplexeren Programmen verliert man leicht den Überblick, welche (beabsichtigten und auch unbeabsichtigten) Nebenwirkungen ein Funktionsaufruf mit Seiteneffekten hat. Man versucht daher, Seiteneffekte möglichst zu vermeiden.

Vermeidung von Seiteneffekten Eine Möglichkeit besteht darin, die Verantwortung für seiteneffektfreie Programme dem Programmentwickler zu überlassen. Dieser muss dann dafür Sorge tragen, dass keine Bausteine mit Seiteneffekten erstellt werden. Eine andere Möglichkeit besteht darin, die Programmiersprache so zu konzipieren, dass keine Seiteneffekte mehr möglich sind. Funktionale Programmierung ist (zumindest in der strengen Version) so konzipiert, dass keine Seiteneffekte auftreten können und dass referentielle Transparenz stets gewährleistet ist.

Referentielle Transparenz Referentielle Transparenz liegt vor, wenn ein Teilausdruck - unabhängig von seiner Position innerhalb eines Ausdrucks und unabhängig vom Zeitpunkt der Auswertung - immer denselben Wert hat. def minimum(a, b): global m while (a != 0) and (b != 0): m = m + 1 a = a - 1 b = b – 1 def minimum(a, b): m = 0 while (a != 0) and (b != 0): m = m + 1 a = a - 1 b = b - 1 return m Unterprogramm mit Seiteneffekt Funktion ohne Seiteneffekt >>> m = 0 >>> minimum(5, 8) >>> m 5 >>> minimum(5, 8) >>> m 10 >>> minimum(5, 8) 5 >>> minimum(5, 8) 5 keine referentielle Transparenz

Dialog über Seiteneffekte I: Hallo! Ich vertrete hier die imperative Programmierung. F: Hallo, und ich vertrete die funktionale Programmierung. I: Stimmt es, dass du keine Seiteneffekte erlaubst? F: Ja, so ist es. I: Und wie machst du das? F: Seiteneffekte entstehen durch Zuweisungen an globale Variablen. Bei mir gibt es gar keine Zuweisungen mehr. Dann kann man auch nicht in die Verlegenheit kommen, einer globalen Variablen etwas zuzuweisen. I: Klingt einfach. Aber, wie soll man dann einen so einfachen Vorgang wie den folgenden ohne Zuweisungen beschreiben? F: Man muss es halt ganz anders machen. I: Das kann ich mir nicht so recht vorstellen. Wenn es keine Zuweisungen gibt, dann macht es auch keinen großen Sinn, Variablen einzuführen. Man hat ja keine Möglichkeit, den Wert der Variablen zu verändern. Und wenn man Werte von Variablen nicht verändern kann, dann kann man es durch Anweisungen im Schleifenkörper auch nicht erreichen, dass die Abbruchbedingung einer SOLANGE-Schleife erfüllt wird. Das macht ja alles keinen Sinn mehr. F: Wieso bist du so auf Variablen und Schleifen fixiert? I: Variablen benötigt man ja wohl zur Datenverwaltung und ohne Schleifen gibt es keine Wiederholungen. Oder sehe ich das ganz falsch? F: Ja, das siehst du etwas zu eng. Wenn du wissen willst, wie mein Berechnungskonzept funktioniert, dann musst du dir den nächsten Abschnitt anschauen. ALGORITHMUS Summenberechnung: {vorher: n aus N} setze s auf 0 setze i auf 0 SOLANGE i <= n: erhöhe s um i erhöhe i um 1 {nachher: s = 0+1+2+...+n}

Exkurs: Seiteneffekte bei Objekten def invertiereBild(L): """ das in L dargestellte Bild wird invertiert d.h.: aus 0 wird 1 und aus 1 wird 0 for i in range(3, len(L)): if L[i] == 0: L[i] = 1 else: L[i] = 0 return L def addBit(i, j): """ die Bits i, j werden add.: 0+0->0;0+1->1;1+0->1;1+1->1 s = i+j if s == 2: s = 1 return s def addBilder(M, N): die Bitfolgen in M und N werden Bit für Bit addiert L = [] for i in range(len(M)): if i < 3: L = L + [M[i]] else: L = L + [addBit(M[i], N[i])] return L

Exkurs: Seiteneffekte bei Objekten >>> bild = ['P1', 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1] >>> negativ = invertiereBild(bild) >>> negativ ['P1', 3, 3, 0, 0, 0, 0, 1, 0, 0, 0, 0] >>> neuesBild = addBilder(bild, negativ) >>> neuesBild ??? Aufgabe: Was erwartest du als Ergebnis beim Testdialog? Warum erhält man ein anderes Ergebnis?

Funktionale Programme Teil 2 Funktionale Programme

Datenverarbeitung mit Funktionen Beispiel: BMI-Berechnung Eingabe der Daten Übergabe der Daten # Eingabe gewicht = float(input("..: ")) groesse = float(input("..: ")) # Verarbeitung bmi = gewicht / (groesse * groesse) # Ausgabe print("BMI:", bmi) def bmi(gewicht, groesse): return \ gewicht/(groesse*groesse) Rückgabe der Daten Wir ersetzen die Eingabe von Daten (durch einen Benutzer) durch die abstraktere Datenübergabe und die Ausgabe der Daten (auf dem Bildschirm) durch die abstraktere Datenrückgabe und konzentrieren uns stärker auf die Verarbeitung der Daten. Ausgabe der Daten >>> Gewicht in kg: 70 Größe in m: 1.70 BMI: 24.2214532872 >>> bmi(70, 1.70) 24.221453287197235

Funktionale Programme Eine Funktion ist eine Einheit, die übergebene Daten verarbeitet und den berechneten Funktionswert als Ergebnis zurückgibt. Die Verarbeitung wird über eine Funktionsdefinition (man sagt oft auch Funktionsdeklaration) festgelegt. Aktiviert wird eine Verarbeitung durch einen Funktionsaufruf. Funktiondefinition def bmi(gewicht, groesse): return \ gewicht/(groesse*groesse) >>> bmi(70, 1.70) 24.221453287197235 Ein funktionales Programm besteht aus einer oder mehreren Funktionsdefinitionen und einem Funktionsaufruf. Funktionsaufruf Eine Funktionsdefinition leistet folgende Festlegungen: die Festlegung der Datenübergabe mit Hilfe von Parameter, die Festlegung der Datenverarbeitung und die Festlegung der Datenrückgabe. Bei einem Funktionsaufruf werden die Parameter mit konkreten Werten versehen. Diese Daten werden dann nach den Festlegungen in der Funktionsdefinition verarbeitet. Abschließend wird ein Funktionswert als Ergebnis zurückgeliefert.

Funktionale Programme In einem rein funktionalen Programm dürfen in den Funktionsdefinitionen außer den Parametern keine weiteren Variablen vorkommen. In der Funktionsdefinition kommen dann auch keine Zuweisungen vor. Hierdurch wird gewährleistet, dass keine Seiteneffekte zustande kommen können. def wegberechnungen(geschwindigkeit): return ((geschwindigkeit/10)*3, (geschwindigkeit/10)**2, (geschwindigkeit/10)*3 + (geschwindigkeit/10)**2) rein funktionales Programm Schwierigkeit: Teilausdrücke wie (geschwindigkeit/10)*3 und (geschwindigkeit/10)**2 kommen mehrfach in der Berechnungsvorschrift vor und müssen bei der Ausführung gegebenenfalls mehrfach berechnet werden. Das ist ineffizient.

keine (neuen) Variablen Lokale Bindungen def wegberechnungen(geschwindigkeit): return ((geschwindigkeit/10)*3, (geschwindigkeit/10)**2, (geschwindigkeit/10)*3 + (geschwindigkeit/10)**2) keine (neuen) Variablen def wegberechnungen(geschwindigkeit): reaktionsweg = (geschwindigkeit/10)*3 bremsweg = (geschwindigkeit/10)**2 anhalteweg = reaktionsweg + bremsweg return (reaktionsweg, bremsweg, anhalteweg) nur lokale Bindungen Ein Ausweg ergibt sich, wenn wir Variablen als lokale Konstanten verwenden. Das bedeutet, dass Variablen nur lokal innerhalb von Funktionsdefinitionen vorkommen dürfen und dass ihnen nur ein einziges mal ein Wert zugewiesen werden darf. Auf der rechten Seite einer Zuweisung darf dann die Variable, die auf der linken Seite der Zuweisung steht, nicht vorkommen. Man spricht in diesem Zusammenhang auch von lokalen Bindungen.

keine (neuen) Variablen Lokale Bindungen def wegberechnungen(geschwindigkeit): return ((geschwindigkeit/10)*3, (geschwindigkeit/10)**2, (geschwindigkeit/10)*3 + (geschwindigkeit/10)**2) keine (neuen) Variablen def wegberechnungen(geschwindigkeit): reaktionsweg = (geschwindigkeit/10)*3 bremsweg = (geschwindigkeit/10)**2 anhalteweg = reaktionsweg + bremsweg return (reaktionsweg, bremsweg, anhalteweg) nur lokale Bindungen Mit lokalen Bindungen wird ein Term lokal (d.h. innerhalb der Funktionsdefinition) an einen Namen gebunden, so dass man diesen Namen an Stelle des Terms benutzen kann. Funktionale Programme mit lokalen Bindungen kann man also jederzeit in streng funktionale Programme umwandeln, ohne das Verhalten des Programms zu verändern. Evtl. wird nur die Berechnungszeit hierdurch beeinflusst. Funktionale Programme mit lokalen Bindungen erzeugen also keine Seiteneffekte und sind - genau wie rein funktionale Programme - referentiell transparent.

Funktionskomposition Beispiel: erstes Zeichen eines Wortes ans Ende verschieben # Eingabe wort = input('Wort: ') # Verarbeitung # entferne das erste Zeichen erstesZeichen = wort[0] wort = wort[1:] # füge es am Ende des Worts ein wort = wort + erstesZeichen # Ausgabe print(wort) def erstesZeichen(wort): return wort[0] def ohneErstesZeichen(wort): return wort[1:] def mitLetztemZeichen(wort, \ zeichen): return wort + zeichen mehrere Aktionen hintereinander ausführen Funktionsaufrufe ineinander schachteln >>> mitLetztemZeichen(ohneErstesZeichen('TOR'), erstesZeichen('TOR')) 'T' 'OR' 'ORT'

Funktionskomposition Bei der Komposition bzw. Verkettung von Funktionen werden Funktionen ineinandergeschachtelt aufgerufen. Beispiel: Zeichen innerhalb eines Wortes verschieben def erstesZeichen(wort): return wort[0] def ohneErstesZeichen(wort): return wort[1:] def mitLetztemZeichen(wort, zeichen): return wort + zeichen def erstesZeichenAlsLetztesZeichen(wort): return mitLetztemZeichen(ohneErstesZeichen(wort), \ erstesZeichen(wort)) Aufgabe: Löse die Probleme in (a) und (b) analog mit einer Funktion, die geeignete Hilfsfunktionen benutzt. (a) In einer nicht-leeren Zeichenkette soll das letzte Element ganz an an den Anfang der Zeichenkette gesetzt werden. Aus 'ORT' soll so 'TOR' erzeugt werden. (b) In einer nicht-leeren Zeichenkette soll das erste mit dem letzten Zeichen ausgetauscht werden. Aus 'TOR' soll so 'ROT' erzeugt werden.

Fallunterscheidungen Bei einer Funktionsdefinition mit Fallunterscheidung werden die Funktionswerte für verschiedene Fälle (evtl. unterschiedlich) festgelegt. Beispiel: erstes Zeichen eines Wortes entfernen WENN das Wort Zeichen enthält: entferne das erste Zeichen SONST: tue nichts def ohneErstesZeichen(wort): if wort != '': return wort[1:] else: return '' Fallunterscheidung Funktionsdefinition mit Fallunterscheidung Aufgabe: Löse die folgenden Probleme mit geeigneten Hilfsfunktionen. Dabei soll auch berücksichtigt werden, dass das Ausgangswort eventuell keine Zeichen enthält. (a) In einer nicht-leeren Zeichenkette soll das letzte Element ganz an an den Anfang der Zeichenkette gesetzt werden. Aus 'ORT' soll so 'TOR' erzeugt werden. (b) In einer nicht-leeren Zeichenkette soll das erste mit dem letzten Zeichen ausgetauscht werden. Aus 'TOR' soll so 'ROT' erzeugt werden.

iterativer Lösungsansatz (benutzt Variablen und eine Schleife) Wiederholungen Beispiel: Wörter umdrehen {neuesWort -> ''; altesWort -> 'LEBEN'} neuesWort = altesWort[0] + neuesWort altesWort = altesWort[1:] {neuesWort -> 'L'; altesWort -> 'EBEN'} {neuesWort -> 'EL'; altesWort -> 'BEN'} {neuesWort -> 'BEL'; altesWort -> 'EN'} {neuesWort -> 'EBEL'; altesWort -> 'N'} {neuesWort -> 'NEBEL'; altesWort -> ''} iterativer Lösungsansatz (benutzt Variablen und eine Schleife) Eingabe: wort altesWort = wort neuesWort = '' SOLANGE altesWort noch Zeichen enthält: füge das erste Zeichen von altesWort vorne in neuesWort ein entferne das erste Zeichen von altesWort Ausgabe: neuesWort

Rekursion Beispiel: Wörter umdrehen umdrehen('LEBEN') -> (umdrehen('EBEN') + 'L') -> ((umdrehen('BEN') + 'E') + 'L') -> (((umdrehen('EN') + 'B') + 'E') + 'L') -> ((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') -> (((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') -> ((((('' + 'N') + 'E') + 'B') + 'E') + 'L') -> (((('N' + 'E') + 'B') + 'E') + 'L') -> ((('NE' + 'B') + 'E') + 'L') -> (('NEB' + 'E') + 'L') -> ('NEBE' + 'L') -> 'NEBEL' rekursiver Lösungsansatz (benutzt Problemreduktionen und Funktionskomposition)

Rekursion Beispiel: Wörter umdrehen Problemreduktionsmuster: umdrehen('LEBEN') -> (umdrehen('EBEN') + 'L') -> ((umdrehen('BEN') + 'E') + 'L') -> (((umdrehen('EN') + 'B') + 'E') + 'L') -> ((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') -> (((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') -> ((((('' + 'N') + 'E') + 'B') + 'E') + 'L') -> (((('N' + 'E') + 'B') + 'E') + 'L') -> ((('NE' + 'B') + 'E') + 'L') -> (('NEB' + 'E') + 'L') -> ('NEBE' + 'L') -> 'NEBEL' Fall 1: wort == '' umdrehen(wort) -> wort Fall 2: wort != '' umdrehen(wort) -> umdrehen(wort[1:]) + wort[0] def umdrehen(wort): if len(wort) == 0: return '' else: return umdrehen(wort[1:]) + wort[0] rekursive Funktionsdefinition

Rekursion Rekursive Problemreduktion ist eine Problemlösestrategie, bei der ein Problem auf ein strukturgleiches Problem (in "verkleinerter" Form) zurückgeführt wird. Eine rekursive Funktionsdefinition ist eine Funktionsdefinition, bei der die Funktion selbst im Funktionsterm benutzt wird. def umdrehen(wort): if len(wort) == 0: return wort else: return umdrehen(wort[1:]) + wort[0]

Ausführung rekursiver Funktionsdef. umdrehen('LEBEN') -> (umdrehen('EBEN') + 'L') -> ((umdrehen('BEN') + 'E') + 'L') -> (((umdrehen('EN') + 'B') + 'E') + 'L') -> ((((umdrehen('N') + 'E') + 'B') + 'E') + 'L') -> (((((umdrehen('') + 'N') + 'E') + 'B') + 'E') + 'L') -> ((((('' + 'N') + 'E') + 'B') + 'E') + 'L') -> (((('N' + 'E') + 'B') + 'E') + 'L') -> ((('NE' + 'B') + 'E') + 'L') -> (('NEB' + 'E') + 'L') -> ('NEBE' + 'L') -> 'NEBEL' Bei der Auswertung von Funktionsaufrufen kommt es bei rekursiven Funktionsdefinitionen zur wiederholten Ausführung strukturgleicher Reduktionsschritte. Rekursion kann somit als Ersatz für die Kontrollstruktur "Wiederholung" benutzt werden. Damit dieses Wiederholungskonzept terminiert, muss nach endlichen vielen Reduktionsschritten eine Situation erreicht werden, bei der die Lösung zum Problem direkt angegeben werden kann. Man versucht daher, Reduktionsschritte so zu konzipieren, dass sie das Problem in einer Weise "verkleinern", die nur endlich viele Verkleinerungsschritte zulässt. Rekursion ist eine mächtige und gerne genutzte Strategie beim Problemlösen. Wenn der Problemkontext es zulässt, dann kann man das Problemlöseverfahren sehr einfach und kompakt über eine Problemreduktion beschreiben. Die wiederholte Ausführung der Reduktionsschritte - und damit die Erzeugung der Lösung - überlässt man dem Ausführsystem.

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

Aufgaben Aufgabe: Wir betrachten die Berechnung der Summe natürlicher Zahlen. ALGORITHMUS Summenberechnung: {vorher: n aus N} setze s auf 0 setze i auf 0 SOLANGE i <= n: erhöhe s um i erhöhe i um 1 {nachher: s = 0+1+2+...+n} Regeln Summenberechnung: summe(0) -> summe(5) -> 5 + summe(4) iterative Lösung rekursive Lösung Entwickle und teste eine hierzu passende Funktionsdefinition.

Aufgaben Aufgabe: Das Problem besteht darin, jedes Element aus einer Zahleniste um einen bestimmten Wert zu erhöhen. Ergänze die folgenden Problemreduktionsschritte und verallgemeinere sie zu einem funktionalen Programm. addiere(3, []) -> addiere(3, [4, 6, 6, 2, 0, 3]) ->

Aufgaben Aufgabe: Das Problem besteht darin, aus einer Zahlenliste alle Elemente herauszufiltern, die kleiner als ein bestimmter Wert sind. Erstelle erst einmal geeignete Problemreduktionsschritte. Verallgemeinere sie anschließend zu einem funktionalen Programm.

Funktionen als Datenobjekte Teil 3 Funktionen als Datenobjekte

Datensätze herausfiltern Immer wieder kommt es vor, dass Datensätze nach vorgegebenen Kriterien aus einem Datenbestand herausgefiltert werden sollen. Alle Tage, an denen der DAX-Wert über der Marke 10000 lag Alle Personen, die ab dem Jahr 2000 geboren wurden

Beispiel: Dax Alle Tage, an denen der DAX-Wert über der Marke 10000 lag def daxUeber10000(liste): if len(liste) == 0: return [] else: if liste[0][1] > 10000: return [liste[0]] + daxUeber10000(liste[1:]) return daxUeber10000(liste[1:]) >>> datenDax = [('2015-06-30', 10044.97), ('2015-06-29', 9987.2), ('2015-06-26', 9940.43), ('2015-06-25', 10012.13), ('2015-06-24', 9995.26)] >>> daxUeber10000(datenDax) [('2015-06-30', 10044.97), ('2015-06-25', 10012.13)] Aufgabe: Analyse den Quelltext und erläutere die Programmlogik.

Beispiel: Sportverein Alle Personen, die ab dem Jahr 2000 geboren wurden def geborenAb2000(liste): if len(liste) == 0: return [] else: if liste[0][2][2] >= 2000: return [liste[0]] + geborenAb2000(liste[1:]) return geborenAb2000(liste[1:]) >>> datenPersonen = [ ('Maximilian', 'Meyer', (21,6,1986)), ('Adriana', 'Müller', (21,1,2001)), ('Silke', 'Schmidt', (13,7,1990)), ('Oliver', 'Schmitt', (14,4,1994)), ('Pia', 'Schwarz', (11,11,2003)), ('Paul', 'Schwarz', (11,11,2003))] >>> geborenAb2000(datenPersonen) [('Adriana', 'Müller', (21, 1, 2001)), ('Pia', 'Schwarz', (11, 11, 2003)), ('Paul', 'Schwarz', (11, 11, 2003))]

Strukturelle Ähnlichkeit def daxUeber10000(liste): if len(liste) == 0: return [] else: if liste[0][1] > 10000: return [liste[0]] + daxUeber10000(liste[1:]) return daxUeber10000(liste[1:]) def geborenAb2000(liste): if len(liste) == 0: return [] else: if liste[0][2][2] >= 2000: return [liste[0]] + geborenAb2000(liste[1:]) return geborenAb2000(liste[1:]) Alle Elemente aus einer Liste herausfiltern, die eine Bedingung erfüllen

Verallgemeinerung def daxUeber10000(liste): if len(liste) == 0: return [] else: if liste[0][1] > 10000: return [liste[0]] + daxUeber10000(liste[1:]) return daxUeber10000(liste[1:]) def myFilter(liste, bedingung): if len(liste) == 0: return [] else: if bedingung(liste[0]) == True: return [liste[0]] + myFilter(liste[1:], bedingung) return myFilter(liste[1:], bedingung) Alle Elemente aus einer Liste herausfiltern, die eine Bedingung erfüllen

Filter-Operator def myFilter(liste, bedingung): if len(liste) == 0: return [] else: if bedingung(liste[0]) == True: return [liste[0]] + myFilter(liste[1:], bedingung) return myFilter(liste[1:], bedingung) def bedingungDaxUeber10000(daxWert): if daxWert[1] >= 10000: return True else: return False >>> datenDax = [('2015-06-30', 10044.97), ('2015-06-29', 9987.2), ('2015-06-26', 9940.43), ('2015-06-25', 10012.13), ('2015-06-24', 9995.26)] >>> myFilter(datenDax, bedingungDaxUeber10000) [('2015-06-30', 10044.97), ('2015-06-25', 10012.13)]

Funktion als Übergabeobjekt Filter-Operator Verarbeitungssituation: Elemente einer Liste, die eine bestimmte Bedingung erfüllen, werden in einer neuen Liste zusammengestellt. Wir benutzen den sogenannen filter-Operator zur Erfassung dieser Verarbeitungssituation. Beispiele zur Anwendung des filter-Operators: Funktion als Übergabeobjekt

Anwendung des filter-Operators Aufgabe: Benutze den Filter-Operator, um alle Personen zu bestimmen, die größer als 1.80 sind. def myFilter(liste, bedingung): … … >>> datenPersonen = [ ('A', 1.76), ('B', 1.91), ('C', 1.66), ('D', 1,84), ('E', 1.99), ('F', 1.80)] >>> … …

Anwendung des filter-Operators Aufgabe: Benutze den Filter-Operator, um alle Personen zu bestimmen, die ab dem Jahr 2000 geboren wurden. def myFilter(liste, bedingung): … … >>> datenPersonen = [ ('Maximilian', 'Meyer', (21,6,1986)), ('Adriana', 'Müller', (21,1,2001)), ('Silke', 'Schmidt', (13,7,1990)), ('Oliver', 'Schmitt', (14,4,1994)), ('Pia', 'Schwarz', (11,11,2003)), ('Paul', 'Schwarz', (11,11,2003))] >>> … [('Adriana', 'Müller', (21, 1, 2001)), ('Pia', 'Schwarz', (11, 11, 2003)), ('Paul', 'Schwarz', (11, 11, 2003))]

Funktionen höherer Ordnung Eine Funktion höherer Ordnung ist eine Funktion, die Funktionen als Übergabedaten erhält oder eine Funktion als Rückgabedatum zurückliefert. def rechnen(f, a, b): return f(a, b) def add(a, b): return a + b def sub(a, b): return a - b >>> rechnen(add, 3, 6) 9 >>> rechnen(sub, 5, 2) 3

Funktionen höherer Ordnung Eine Funktion höherer Ordnung ist eine Funktion, die Funktionen als Übergabedaten erhält oder eine Funktion als Rückgabedatum zurückliefert. def myFilter(liste, bedingung): if len(liste) == 0: return [] else: if bedingung(liste[0]) == True: return [liste[0]] + myFilter(liste[1:], bedingung) return myFilter(liste[1:], bedingung) def bedingungDaxUeber10000(daxWert): if daxWert[1] >= 10000: return True else: return False >>> datenDax = … >>> myFilter(datenDax, bedingungDaxUeber10000) …

List-Comprehension def myFilter(liste, bedingung): return [e for e in liste if bedingung(e)] def bedingungDaxUeber10000(daxWert): return daxWert[1] >= 10000 >>> datenDax = [('2015-06-30', 10044.97), ('2015-06-29', 9987.2), ('2015-06-26', 9940.43), ('2015-06-25', 10012.13), ('2015-06-24', 9995.26)] >>> myFilter(datenDax, bedingungDaxUeber10000) [('2015-06-30', 10044.97), ('2015-06-25', 10012.13)]

List-Comprehension List-Comprehension (bzw. Listenabstraktion) dient dazu, Listen auf eine sehr einfache Weise zu konstruieren. >>> quadratzahlen = [x**2 for x in range(10)] >>> quadratzahlen [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> geradeQuadratzahlen = [x for x in quadratzahlen if x % 2 == 0] >>> geradeQuadratzahlen [0, 4, 16, 36, 64] >>> quadratzahlen = [x**2 for x in range(10)] >>> quadratzahlen [0, 1, 4, 9, 16, 25, 36, 49, 64, 81] >>> geradeQuadratzahlen = [x for x in quadratzahlen if x % 2 == 0] >>> geradeQuadratzahlen [0, 4, 16, 36, 64]

Anonyme Funktionen def myFilter(liste, bedingung): return [e for e in liste if bedingung(e)] >>> datenDax = [('2015-06-30', 10044.97), ('2015-06-29', 9987.2), ('2015-06-26', 9940.43), ('2015-06-25', 10012.13), ('2015-06-24', 9995.26)] >>> myFilter(datenDax, lambda e: e[1] >= 10000) [('2015-06-30', 10044.97), ('2015-06-25', 10012.13)]

Lamda-Operator Der Lambda-Operator dient dazu, Funktionen zu definieren. Es gibt zwei Möglichkeiten in Python, Funktionen zu erstellen. def add(a, b): return a + b def sub(a, b): return a - b add = lambda x, y: x+y sub = lambda x, y: x-y Funktionsdefinition mit def Funktionsdefinition mit lambda-Operator >>> add(3, 4) 7 >>> sub(6, 3) 3 >>> add(3, 4) 7 >>> sub(6, 3) 3 Man benutzt ihn in Situationen, in denen eine Funktion nur einmal benutzt wird und der Funktionsname daher keine Rolle spielt. Siehe auch: https://pythonconquerstheuniverse.wordpress.com/2011/08/29/lambda_tutorial/

Übungen Aufgaben (a) Was leistet die Funktion myMap? def myMap(liste, f): if liste == []: return [] else: return [f(liste[0])] + myMap(liste[1:], f) def myMap(liste, f): return [f(x) for x in liste] Aufgaben (a) Was leistet die Funktion myMap? (b) Teste sie mit geeigneten Daten. (c) In welchen Situationen könnte sie von Nutzen sein?

Übungen Im Sportverein steht die Berechnung des jährlichen Mitgliederbeitrags an. Zwei Beitragberechnungsmodelle sollen durchgespielt werden. Im ersten Modell ist der Beitrag nur grob gestaffelt: Kinder, Jugendliche und Erwachsene zahlen jeweils einen festen Betrag. Im zweiten Modell ist der Beitrag direkt an das Alter (am Ende des Kalenderjahres) gekoppelt. Löse mit geeigneten funktionalen Programmen.

Deklarative Programmierung Teil 4 Deklarative Programmierung

Deklarative Programmierung Problem: Bestimme alle Tage, an denen der DAX-Wert über der Marke 10000 lag. Lösung: def myFilter(liste, bedingung): return [e for e in liste if bedingung(e)] >>> datenDax = […] >>> myFilter(datenDax, lambda e: e[1] >= 10000) …

Dialog über Datenverarbeitung I: Hallo! Ich vertrete hier die imperative Programmierung. F: Hallo, und ich vertrete die funktionale Programmierung. I: Neulich hast du behauptet, dass man Programme ohne Variablen und Zuweisungen schreiben kann und dass man keine Schleifen benötigt. F: Das stimmt - zumindest so ungefähr! I: Ich kann mir das immer noch nicht so recht vorstellen. Wie soll das z.B. bei einem ganz einfachen Algorithmus wie dem folgenden funktionieren? F: Umdenken ist die Devise! Der Algorithmus benutzt Befehle, um die Daten zu verarbeiten. Die Daten werden dabei mit Hilfe von Variablen verwaltet. Ich mache das ganz anders. Ich konzipiere eine Funktion, die das Gewünschte leistet, und definiere sie mit Hilfe von Regeln. I: Sehe ich das richtig, dass da auch Variablen vorkommen? F: Ja, aber nur als Parameter, um Daten an die Funktion zu übergeben. Der entscheidende Unterschied ist, dass sie ihre Werte nicht verändern. I: Das ist ja alles schön und gut. Aber, wozu soll das Ganze gut sein? Ich komme mit meiner Methode doch auch zum Ziel. F: Stimmt, wir kommen beide zum Ziel, aber mit ganz unterschiedlichen Methoden. Du benutzt Algorithmen, die letztlich aus Anweisungen bzw. Befehlen bestehen. Diese Befehle richten sich an eine tatsächliche oder gedachte Maschine. Bei der Entwicklung von Algorithmen musst du also denken wie eine Maschine. I: Das habe ich zwar noch nie so empfunden, aber eigentlich hast du recht. Und wie denkst du? ALGORITHMUS Summenberechnung: {vorher: n aus N} setze s auf 0 setze i auf 0 SOLANGE i <= n: erhöhe s um i erhöhe i um 1 {nachher: s = 0+1+2+...+n} FUNKTION summe: {Fall 1:} n == 0: summe(n)->0 {Fall 2:} n > 0: summe(n)->n+summe(n-1)

Dialog über Datenverarbeitung F: Ich entwickle Beschreibungen anstatt Befehle zu erteilen. Ich beschreibe das gewünschte Berechnungsverhalten mit einer Funktion, die den Ausgangsdaten die Zieldaten zuordnet. Anschließend lege ich mit Hilfe von Regeln die Zuordnung präzise fest. I: Das erinnert mich irgendwie an Funktionsdefinitionen in der Mathematik. F: Ja, da macht man das ganz genauso. I: Und warum sollen wir beim Programmieren vorgehen wie Mathematiker? Hat das irgendwelche Vorteile? F: Beim Programmieren macht man leicht Fehler. Gib zu, dass du auch schon mal die Anweisung "erhöhe i um 1" vergessen hast. Wenn man funktional programmiert, kann man sich leichter von der Korrektheit der Programme überzeugen. Ich mache mir die Korrektheit von Regeln immer anhand typischer Beispiele klar: I: Ok, das sehe ich ein! Aber, wenn ich mich so umschaue, stelle ich fest, dass fast alle imperativ programmieren wie ich. So richtig durchgesetzt hast du dich noch nicht, oder? F: Viele kennen mich halt nicht - das ist irgendwie schade. Die, die mich kennen, benutzen meinen deklarativen Ansatz beim Entwickeln von Prototypen. Es geht hier darum, schnell ein System zu entwickeln, das korrekt arbeitet bzw. das gewünschte Verhalten zeigt. Es kommt nicht darauf an, das Laufzeitverhalten zu optimieren oder Benutzerfreundlichkeit zu gewährleisten. I: Kannst du das an einem Beispiel klarmachen? F: Gerne, aber erst im nächsten Abschnitt. FUNKTION summe: {Fall 1:} n == 0: summe(n)->0 {Fall 2:} n > 0: summe(n)->n+summe(n-1)

Beschreiben statt Befehlen Imperative Programmierung besteht darin, eine (mehr oder weniger abstrakte) Maschine mit Hilfe von Anweisungen zu steuern. Dabei wird beschrieben, wie die Ergebnisse berechnet werden sollen. Zentrale Bausteine imperativer Programme sind Wertzuweisungen, die i.a. den momentanen Variablenzustand (Speicherzustand) verändern. Imperative Programmierung ist wegen der Möglichkeit, Seiteneffekte zu produzieren, recht fehleranfällig. Deklarative Programmierung besteht darin, den Problemkontext (die Miniwelt) mit gegebenen Mitteln zu beschreiben. Funktionale Programmierung - als eine Form der deklarativen Programmierung - benutzt hierzu Funktionen. Bei der deklarativen Programmierung wird somit beschrieben, was in der Modellwelt gelten soll. Die funktionale Programmierung arbeitet dabei ohne Speichervariablen. Variablen kommen hier nur als Funktionsvariablen zur Übergabe von Funktionsargumenten vor. Seiteneffekte sind demnach in der funktionalen Programmierung nicht möglich. Das Verhalten einer Funktion wird vollständig durch die Funktionsdeklarationen festgelegt. Funktionale Programmierung erfolgt auf einem höheren Abstraktionsniveau. Es ergeben sich dadurch Programme, die kürzer, leichter zu durchschauen und weniger fehleranfälliger sind. Funktionale Programmierung eignet sich zum „Prototyping“.

Prototyping Bei der Entwicklung neuer Produkte wird zunächst oft ein Prototyp erstellt. Dabei handelt es sich um ein funktionsfähiges Modell, das dem geplanten Produkt in vielen Bereichen entspricht, meist aber auch eine Reihe von Vereinfachungen aufweist. Prototypen werden - insbesondere bei komplexen technischen Systemen - zunächst einmal erstellt, um Erfahrungen mit dem neuen System und seiner Entwicklung zu sammeln. Prototypen kommen entsprechend auch bei der Entwicklung komplexer Software zum Einsatz. Wir skizzieren im Folgenden, wie ein Prototyp zu einem RSA-Kryptosystem mit Hilfe der funktionalen Programmierung erstellt werden kann.

Prototyp zu einem Kryptosystem Beim RSA-Verfahren werden natürliche Zahlen mit Hilfe eines (öffentlichen bzw. privaten) Schlüssels in neue Zahlen umwandelt. from kryptosystem_rsa import * # Testprogramm n = 116063 e = 4097 d = 51137 oeffentlicherSchluessel = (e, n) privaterSchluessel = (d, n) s0 = 'Köln' print(s0) s1 = verschluesseln(s0, oeffentlicherSchluessel, codierenZeichenketteZahlenliste) print(s1) s2 = entschluesseln(s1, privaterSchluessel, decodierenZahlenlisteZeichenkette) print(s2) >>> Köln [99070, 100824, 50230] Die Funktionen zur Realisierung des Prototyps findet man auf den Seiten von www.inf-schule.de (1.22.4.2).

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) [Lorenz 12] G. Funktionale Modellierung und Rekursion. Oldenbourg Verlag 2012. …