Eine rein funktionale Sprache

Slides:



Advertisements
Ähnliche Präsentationen
ALP II: Objektorientierte Programmierung Sommersemester 2006
Advertisements

der Universität Oldenburg
Forschungszentrum caesar
Definition von Klassen in Java
Einführung in die Programmierung Zusammenfassung
Programmieren in Logik
Lineare Suche Divide-and-Conquer-Suche Kombinationssuche
Suche in Texten (Stringsuche )
Kapitel 6. Suchverfahren
10. Grundlagen imperativer Programmiersprachen
Java: Objektorientierte Programmierung
Sortierverfahren Richard Göbel.
Java: Dynamische Datentypen
Sortierverfahren Richard Göbel.
Indirekte Adressierung
FH-Hof Verwaltung von Zeichenketten Richard Göbel.
Java: Referenzen und Zeichenketten
Java: Grundlagen der Objektorientierung
Konstruktoren.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (03 – Verschiedene Algorithmen für dasselbe Problem) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Tobias Lauer.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (03 – Verschiedene Algorithmen für dasselbe Problem) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (02 – Funktionenklassen) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 2 Algorithmen und Datenstrukturen (27 – Kürzeste Wege) Prof. Th. Ottmann.
Informatik II, SS 2008 Algorithmen und Datenstrukturen Vorlesung 4 Prof. Dr. Thomas Ottmann Algorithmen & Datenstrukturen, Institut für Informatik Fakultät.
1 Vorlesung Informatik 2 Algorithmen und Datenstrukturen (03 – Verschiedene Algorithmen für dasselbe Problem) Prof. Dr. Th. Ottmann.
Vorlesung Informatik 3 Einführung in die Theoretische Informatik (17 –Turingmaschinen) Prof. Dr. Th. Ottmann.
Deklaratives Debugging (Seminar Software Engineering) Tim Sender Deklaratives Debugging Seminar Software Engineering.
Robotik mit LEGO Mindstorms
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
Universität Dortmund, Lehrstuhl Informatik 1 EINI II Einführung in die Informatik für Naturwissenschaftler und Ingenieure.
Imperative Programmierung Funktionen und Parameter
PKJ 2005/1 Stefan Dissmann Zusammenfassung Bisher im Kurs erarbeitete Konzepte(1): Umgang mit einfachen Datentypen Umgang mit Feldern Umgang mit Referenzen.
Zusammenfassung Vorwoche
Christian Schindelhauer
Visualisierung funktionaler Programme
Grundkonzepte Java - Klassendefinition
Informatik 1 Übung 2.
Rekursion mit Listen: Quicksort
Grundlagen der Programmierung
Einführung in die Programmierung Wintersemester 2008/09 Prof. Dr. Günter Rudolph Lehrstuhl für Algorithm Engineering Fakultät für Informatik TU Dortmund.
Javakurs FSS 2012 Lehrstuhl Stuckenschmidt
Polynome und schnelle Fourier-Transformation
Einführung in die Programmierung
Einführung in die Programmiersprache C 4
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
Programmieren in C Einführung
Programmiersprachen Proseminar Grundlagen wissenschaftlichen Arbeitens
Unterprogramme in JAVA
Algorithmen und Datenstrukturen Übungsmodul 8
3. Lineare Befehle 3.1 Die Ausgabefunktion
Programmieren in C Grundlagen C 2
BMEVIEEA100 Grundlagen der Programmierung
ENDLICHE KÖRPER RSA – VERFAHREN.
PHP: Operatoren und Kontrollstrukturen
Informatik III Christian Schindelhauer Wintersemester 2006/07
SFZ FN Sj. 13/14 Python 3 Rekursion Inf K1/2 Sj 13/14
Java Syntaxdiagramme Buchstabe A B Z a z ... Ziffer
Java-Kurs - 4. Übung Hausaufgabe Weitere Kontrollstrukturen
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
Mag. Thomas Hilpold, Universität Linz, Institut für Wirtschaftsinformatik – Software Engineering 1 Algorithmen und Datenstrukturen 1 SS 2002 Mag.Thomas.
Variablen und Datentypen
Funktionen. Aufgabe : Eingabe zweier Zahlen ---> Minimum bestimmen Dann nochmals Eingabe zweier Zahlen ---> Minimum bestimmen.
Dr. Wolfram Amme, Semantik funktionaler Programme, Informatik II, FSU Jena, SS Semantik funktionaler Programme.
«Delegierter» Methoden Schablone Funktionszeiger
 Präsentation transkript:

Eine rein funktionale Sprache Haskell Eine rein funktionale Sprache Programmiersprache der 3. Generation

Ein Rat vorweg An die anwesenden C-Programmierer: Vergesst am besten alles, was Ihr bisher über Programmierung und Programmiersprachen gelernt habt !!! 

We now proudly present:

Übersicht Einführung Einordnen von Haskell in das Sprachen-Spektrum imperativ vs. deklarativ Beispiel eines imperativen Programms und eines deklarativen Programms Entwicklung der deklarativen Sprachen, insb. Haskell l-Kalkül, Grundlage von Haskell Einführung, Auswertung, Erweiterung, Beispiele, … Evolution der Programmiersprachen Grundkonzepte funktionaler Sprachen, insb. Haskell Typen Beispielprogramm: Kryptographie Auswertung Beispielauswertung (Tafel) Lazy Evaluation Rekursive Funktionen (mit einfachen Datenstrukturen) Kurze Übersicht über das heutige Thema, Der erste Teil wird etwas theoretischer sein und ca 20 Minuten dauern, Danach gehen wir konkrete Befehle und Typen der Programmiersprache durch und programmieren unseres erste kleine Programm

Das Sprachen-Spektrum

imperativ vs. deklarativ Merkmale imperativer Sprachen Die Grundidee ist die des „von Neumann“ bzw. des „stored program“ Rechners. Hier werden Programme als feste Folge von Befehlen nacheinander abgearbeitet. Ein Programm beschreibt, wie etwas berechnet wird. Dies geschieht als Abfolge lokaler Speichertransformationen (Wertzuweisungen). Die Kontrollstruktur ist die Iteration (Schleife). Merkmaler deklarativer Sprachen Die Grundidee ist das l-Kalkül von Church, eine mathematische Theorie. Jede Berechnung ist das Ergebnis einer Funktion und wird durch einfache Ersetzung von Ausdrücken errechnet (Reduktion).  vgl. Auswertung Die Hauptkontrollstruktur ist die Rekursion.

Somit lässt sich der Hauptunterschied zusammenfassen als Motto: WAS anstelle von WIE Der Programmierer soll nur noch angeben, was er programmieren möchte, er soll nicht mehr damit belastet werden, wie etwas genau programmiert wird. (Aber trotzdem muss er in der Lage sein dem Computer mitzuteilen, was er haben möchte.)

Der Quicksortalgorithmus Vergleich zwischen imperativer und funktionaler Programmierung Implementierung in Java: private void swap(Object [] rgo, int i, int j) { Object o; o = rgo[i]; rgo[i] = rgo[j]; rgo[j] = o; } protected boolean lessThan(Object oFirst, Object oSecond) return oFirst.hashCode() < oSecond.hashCode(); { public void sort(Object [] rgo) sort(rgo, 0, rgo.length - 1); } private void sort(Object [] rgo, int nLow0, int nHigh0) int nLow = nLow0; int nHigh = nHigh0; Object oMid; if (nHigh0 > nLow0) oMid = rgo[ (nLow0 + nHigh0) / 2 ]; while(nLow <= nHigh) while((nLow < nHigh0) && lessThan(rgo[nLow], oMid)) ++nLow; while((nLow0 < nHigh) && lessThan(oMid, rgo[nHigh])) --nHigh; if(nLow <= nHigh) swap(rgo, nLow++, nHigh--); if(nLow0 < nHigh) sort(rgo, nLow0, nHigh); if(nLow < nHigh0) sort(rgo, nLow, nHigh0); Implementierung in Haskell: quicksort :: Ord a => [a] -> [a] quicksort [] = [] quicksort (pivot:xs) = quicksort [y | y <- xs, y<=pivot] ++ [pivot] ++ quicksort [y | y <- xs, y>pivot] Kurz anmalen, wie der Quicksort funktioniert, als Schaubild: |---------x------|-------------------------------------------| = [a]  Elemente <x und Elemente >x [y| y <-xs, y<pivot] =|-----------------| ++[pivot]++ |-------------------| =[y| y <-xs, y>pivot] Quicksort rekursiv auf Teillisten anwenden Rekursionsanker: leere Listen sind schon geordnet dann noch aneinanderketten der Listen (vgl. PI3,S11)

Jahreszahl Theorie / Sprache Entwickler 1932 l-Kalkül (siehe weiter hinten) Ursprung und gemeinsamer Kern aller funktionalen Sprachen. Churchs Intention bei der Entwicklung des l-Kalküls war eine „Formalisierung des Berechenbarkeitsbegriffs auf der Basis von Funktionen“  „Churchsche These“. Diese These wurde durch die äquivalente Turing-Berechenbarkeit untermauert. Church, Kleene 1960 LISP (List Prozessing) Lisp war die erste funktionale Sprache, die von den Prinzipien der damaligen durch Fortran geprägten Programmierung abwich. Wesentliche Neuheiten von LISP waren: bedingte Ausdrücke, Verwendung von Listen als Basisstruktur, Heapverwaltung mit Garbage Collection John McCarthy 1975 ML (Meta Language) Das Bedeutendste in ML ist das mächtige polymorphe Typenkonzept, das von den meisten modernen funktionalen Sprachen adaptiert wurde. (vgl Abschnitt Typen) Milner, Gordon, University of Edinburgh 1980 Hope In Hope wurden zum ersten Mal benutzerdefinierte Datenstrukturen und Pattern Matching bei der Definition von Funktionen über solchen Strukturen zugelassen. R. Burstall, D. MacQueen, D. Sannella, Uni. of Edinburgh

D. Turner, University of Kent Jahreszahl Theorie / Sprache Entwickler 1985 Miranda Eine der wenigen kommerziell vertriebenen funktionalen Sprachen. Besonders ist hier der Einsatz von Funktionalen (Funktionen höherer Ordnung), sowie die bedarfsgesteuerte Auswertungsstrategie „lazy evaluation“ D. Turner, University of Kent ~1988 Haskell ( Vorname des Logikers Haskell Brooks Curry ) Haskell ist nach dem amerikanischen Logiker Haskell Brooks Curry benannt (weil Haskell [die Sprache] curryfizierte Funktionen liebt, wie Haskell [der Logiker] Haskell wurde von einem Komitee von Forschern entwickelt, die das Ziel verfolgten eine rein funktionale Programmiersprache einzuführen. Haskell unterstützt eine enrome Vielfalt von (auch oben aufgeführten) Konzepten und zeichnet sich insbesondere durch ein mächtiges Typsystem und eine saubere Modellierung von interaktiven Ein-/Ausgaben aus. Bemerkenswert ist, dass eine vollständige formale Semantikdefinition (http://haskell.org/report/index.html) existiert. Ziele beim Entwurf von Haskell waren: Für Lehre, Forschung und Anwendungen, insbesondere für Programmierung großer Systeme Vollständige Beschreibung von Syntax und Semantik Frei erhältlich Basiert auf allgemein akzeptierten Ideen Komitee unter Leitung von P.Hudak (Yale University), Ph. Wadler (Glasgow University) Logiker H. B. Curry Curry ~ zusammenfassen, Tupel bilden, … curry :: ((a,b) -> c) -> a -> b -> c

Entwicklung Zur Geschichte: 1988 Gründung des Haskell-Komitees (auf der FPCA), dem u.a. Paul Hudak, Simon Peyton Jones und Philip Wadler angehören. 1990 Haskell 1.0 Sprachdefinition 1992 Haskell 1.2 Sprachdefinition 1996 Haskell 1.3 Sprachdefinition 1997 Haskell 1.4 Sprachdefinition 1999 Haskell 98 Sprachdefinition

l - Das Lambda-Kalkül Das Lambda-Kalkül besteht aus zwei Bausteinen Funktionsabstraktion lx.A definiert eine (anonyme) Funktion, die ein x bekommt, und einen Ausdruck A als Funktionskörper hat (in dem in der Regel x vorkommt, aber nicht vorkommen muß) Funktionsapplikation FA bedeutet, daß die Funktion F auf dem Ausdruck A angewandt wird Die Groß- u. Kleinschreibung dient nur der Übersicht, es gibt keinen Grund Funktionen und Variablen zu unterscheiden, denn es kommt auf den Kontext an.

Beispiele für Funktionen (1) Die Identität: lx.x Eine Funktion, die jedes Argument auf die Identitätsfunktion abbildet: ly.(lx.x) Die Identität, angewandt auf sich selbst: (lx.x)(ly.y)  (ly.y)

Beispiele für Funktionen (2) Nicht alle Variablennamen müssen definiert sein, auch die folgenden sind korrekte l-Terme lx.Fx (ly.y)(z)  ? uv lk.Jm

Beispiele für Funktionen (3) Ein komplexerer Ausdruck (lf.(lx.f(fx)))uv (lx.u(ux))v u(uv) (lf.(lx.f(fx)))uv vorher vortragen!!!! Frage: Was macht diese Funktion? Diese Funktion wendet also eine Funktion zweimal auf ein Argument an.

Eigenschaften des l-Kalküls Als Bausteine gibt es nur Funktionsabstraktionen und -applikationen. Das ist alles! Es gibt keine Zahlen, Funktionsnamen, arithmetische Funktionen, Wahrheitswerte Die Funktionen werden nicht benannt, sie sind anonym. (Alle heißen l) Das Lambda-Kalkül ist ungetypt

Kalkül Definition Ein Kalkül besteht aus zwei wesentlichen Teilen: Der Kalkülsprache, d.h. einem zugrunde liegenden Alphabet von Zeichen einer Definition der wohlgeformten Ausdrücke Dem Deduktionsgerüst, d.h. Axiomen aus der Menge der wohlgeformten Ausdrücke (Terme) Ableitungsregeln, mit denen Ausdrücke umgeformt/ausgewertet werden nach K.Schröter(1941) Diese Seite soll hauptsächlich der Erklärung des Namens Lambda-Kalkül dienen. Deshalb auch keine Definition von Alphabet, Zeichen, Ausdruck, …

kurzer weiterer Ausblick Auf den ersten Blick bietet das l-Kalkül nur sehr primitive Funktionen, die nicht von praktischem Nutzen sind. Man kann allerdings mit dem l-Kalkül Fallunterscheidungen, Zahlen und arithmetische Funktionen herleiten

kurz notiert(1) Um Zahlen zu erhalten, werden wir jetzt ganz bestimmte Funktionen mit den natürlichen Zahlen identifizieren: FnA (n-malige Anwendung von F auf A) entspricht der induktiven Definition F0A:=A Fn+1A:= F(FnA) Die Zahlen z0,z1,… werden dann definiert durch: zn:=lfx.fn(x) Frage: Was entspricht der 3? l fx.f(f(f(x))) Frage: Was entspricht der 0? l fx.x Was entspricht der 42?

lzz‘fx.zf(z‘fx) kurz notiert(2) Definition von Plus: für zwei Zahlen z und z‘ lzz‘fx.zf(z‘fx) Definition von Mal lzz‘fx.z(z‘f)x Beispiel Z2 und z3 z2=(lfx.f(f(x))) z3=(lfx.f(f(f(x)))) Plus: (zz‘fx.zf(z‘fx)) Z2 z3 z‘fx.z2 f(z‘fx)) z3 z‘fx.f(f(z‘fx))) z3 fx.f(f(f(f(f(x))))) = z5 Eine Beispielrechnung für die Multiplikation sei als Übungsaufgabe dem geneigten Studenten überlassen 

Beispiel zur Addition (verkürzt) Beispiel: Plus z2 z3  z5 z2=(λ f x . f (f (x))) z3 = (λ f x . f (f (f (x)))) Plus z2 z3 (λ z z’ f x . z f (z’ f x)) z2 z3 → (λ z’ f x . z2 f (z’ f x)) z3 → (λ z’ f x . f ( f (z’ f x))) z3 → (λ f x . f ( f (z3 f x))) → (λ f x . f ( f ( f ( f ( f (x)))))) ( = z5 )

Beispiel zur Addition (ausgeschrieben) Am Beispiel: Plus z2 z3 (→ z5 ) Plus z2 z3 (λ z z’ f x . z f (z’ f x)) (λ g y . g (g (y))) (λ g y . g ( g ( g (y)))) → (λ z’ f x . (λ g y . g (g (y))) f (z’ f x)) (λ g y . g ( g ( g (y)))) → (λ z’ f x . (λ y . f ( f (y)) (z’ f x)) (λ g y . g ( g ( g (y)))) → (λ z’ f x . f ( f (z’ f x))) (λ g y . g ( g ( g (y)))) → (λ z’ f x . f ( f (λ g y . g ( g ( g (y))) f x))) → (λ f x . f ( f (λ y . f ( f ( f (y))) x))) → (λ f x . f ( f ( f ( f ( f (x)))))) ( = z5 )

kurz notiert(3) Es ist außerdem möglich If-Anweisungen mit dem l-Kalkül auszudrücken. Sei True := lxy.x (Projektion auf das erste Argument) Sei False := lxy.y (Projektion auf das zweite Argument) Damit können wir ein If definieren: If .. then .. else .. entspricht hier: lbxy.bxy wobei b entweder True oder False ist.

Resultat aus der Theoretischen Informatik Das l-Kalkül ist logisch äquivalent zu der Turing-Maschine. Somit kann man mit dem l-Kalkül (zumindest theoretisch) genau die Probleme lösen (ausrechnen), die die heutigen Computer auch lösen können. Jede von einem Computer berechenbare Funktion ist auch im Lambda-Kalkül berechenbar.

Evolution der Programmiersprachen Problemspezifikation Programm Rechner „high level“ PS Assembler Maschinensprache vertical migration

Grundkonzepte funktionaler Sprachen, insb. Haskell Die meisten Anwendungsprobleme sind auf natürliche Weise als Funktionen definiert, Bsp.: sin x, exp, … LATEX: Datenbanksysteme (SQL): EVA-Prinzip f1 Eingaben Ausgabe fn Funktion

Grundkonzept Haskell Auch Haskell arbeitet so: Ein funktionales Programm ist eine Menge von Funktionsdefinitionen f1 X1 .. Xn = e1 … fr X1,r .. Xn,r = er Funktionsbezeichner Parametervariablen Rumpfausdruck Eine Funktion transformiert Eingabe in Ausgabe Ein besonderer Vorteil dieses Konzepts ist die Tatsache, dass es keine Seiteneffekte geben kann. Dadurch, dass es keine globalen Variablen gibt, werden Funktionen nur durch ihre Funktionsparameter bestimmt und die Ausgabe ist somit zu jedem beliebigen Zeitpunkt des Programmablaufs identisch. Dies erhöht die Übersicht enorm. Theoretisch kann man jede Programmiersprache so benutzen, dass sie zu einer rein funktionalen Sprache wird. Allerdings ist die Ausführung dann nicht besonders effektiv.

Beispiele in Haskell -- hier stehen Kommentare -- einfache arithmetische Funktionen add x1 x2 = x1 + x2 square x = x * x negate x = -x -- Konstantendefinitionen e = 2.71828 newline = '\n' -- Test auf Identität: Vergleichoperatoren allEqual n m p = (n==m) && (m==p) -- Maximumsfunktion: Bsp. für Fallunterscheidung max a b | a>b = a | a==b = a | otherwise = b Bei Konstanten handelt es sich trotzdem um Funktionen, allerdings mit 0 Parametern.

Typen In Haskell haben alle Datenobjekte einen wohldefinierten Typ. Ein Typ ist eine Menge von Objekten gleicher Art, z.B. existieren Basistypen wie Int = Menge aller ganzen Zahlen (32 Bit) Integer = Menge beliebiger ganzer Zahlen, insb. nur durch den Speicher beschränkte große Zahlen Bool = {True, False} Char = Menge aller Zeichen String = Eine Liste von Char Neben diesen Basistypen gibt es auch Funktionstypen, wie z.B.: Int  Int = Menge aller einstelligen Funktionen über den ganzen Zahlen (wie z.B.: Quadrieren): Int  Int  Int = Menge aller zweistelligen Funktionen über den ganzen Zahlen (wie z.B.: Addieren): In Haskell- Programmen können optional Typdeklarationen der Form name :: type zu Definitionen angegeben werden. Bei der Compilation von Programmen erfolgt eine automatische Typinferenz und Typüberprüfung (type checker). Nur korrekt typisierbare Programme können somit compiliert werden. Hierdurch werden viele Programmfehler frühzeitig erkannt. Ist keine konkrete Typisierung vorgenommen, so bestimmt der Compiler automatisch den allgemeinsten Typ für jedes Datenobjekt. Beispiele einblenden

Beispiele in Haskell -- hier stehen Kommentare -- einfache arithmetische Funktionen add :: Int -> Int -> Int add x1 x2 = x1 + x2 square :: Int -> Int square x = x * x negate :: Int -> Int negate x = -x -- Konstantendefinitionen e :: Double e = 2.71828 newline :: Char newline = '\n' -- Test auf Identität allEqual :: Int -> Int -> Int -> Bool allEqual n m p = (n==m) && (m==p) -- Maximumsfunktion? Fallunterscheidung max :: Double -> Double -> Double max a b | a>b = a | a==b = a -- bedingte Auswertungen, funktioniert wie | otherwise = b -- eine if/case-Struktur, das erste passende wird -- ausgewertet. Otherwise ist also im Grunde nichts anderes als die Konstante True. Hier kann ich nochmals auf die Klammern eingehen, insbesondere auf den Unterschied zwischen add :: Int -> Int -> Int add x1 x2 = x1 + x2

Typen Bei der Compilation von Programmen erfolgt eine automatische Typinferenz und Typüberprüfung (type checker). Nicht typisierte Programme werden automatisch möglichst allgemein typisiert. Ist ein Programm im Hugs-System geladen, kann man mit :t <Funktionsbezeichnung> die Typdeklaration eines Objekts erfahren. z.B.: Möglichkeiten, kann der sich Irren

Beispielprogramm Kryptographie (1) Als Beispielprogramm betrachten wir das Caesar-Verschlüsselungsverfahren. Hierzu stellt man sich das Alphabet (im einfachen Fall nur die kleinen Buchstaben) im Kreis angeordnet vor. Ordnet man den Zeichen von a bis z die Nummern 0 bis 25 zu, so ist das Verschieben innerhalb des Kreises eine Addition modulo 26. Die Verschlüsselung besteht darin, anstelle jedes Klartextbuchstabens das Zeichen auszugeben, das im Kreis um eine festgelegte Anzahl von Positionen später kommt. Veranschaulichung

Beispielprogramm Kryptographie (2) Somit ergibt sich die Funktion schiebe: Als nächstes müssen wir zwei Übergangsfunktionen zwischen den Zahlen und den Zeichen definieren schiebe :: Int -> Int -> Int schiebe schluessel zahl = mod (zahl + schluessel) 26 Hier kann ich die Studenten versuchen lassen, den Sinn von schiebe und die Typdefiniton zu erklären. Als nächstes soll man ein Beispiel rechnen. Jetzt sollen die Studenten selber die Funktion position (die einem Zeichen eine Zahl) und die Funktion buchstabe (die einer Zahl ein Buchstaben) zuordnet programmieren (Tafel, jemand oder ich) dazu gibt es folgende 2 Funktionen: ord :: Char -> Int chr :: Int -> Char position :: Char -> Int position zeichen = (ord zeichen) - 97 buchstabe :: Int -> Char buchstabe zahl = chr (zahl + 97)

Beispielprogramm Kryptographie (3) Als nächstes definieren wir eine Funktion caesar:: Int -> Char -> Char die mit einem Schlüssel ein Zeichen verschlüsselt. Dazu verwenden wir die vorigen Funktionen. caesar :: Int -> Char -> Char caesar schluessel zeichen = buchstabe (schiebe schluessel (position zeichen))

Beispielprogramm Kryptographie (4) Die Verschlüsselung eines ganzen Wortes erfordert die zeichenweise Anwendung dieser Funktion auf die Buchstaben des Wortes. Hierzu lässt sich die Funktion map verwenden, mit der eine Funktion elementweise auf eine Zeichenkette angewendet werden kann. Eckige Klammern um den Typ Char signalisieren, dass es sich um eine Liste von Char handelt (also um einen String). caesar_wort :: Int -> [Char] -> [Char] caesar_wort schluessel wort = map (caesar schluessel) wort Bemerkenswert ist hier die Tatsache das caesar schluessel als eine einstellige Funktion betrachtet wird (Da der Erste von den beiden Parametervariablen bereits fest dasteht).

Beispielprogramm Kryptographie (5)

Auswertung Bei der Auswertung von Ausdrücken werden die Funktionsdefinitionen als Ersetzungsregeln interpretiert. In einem Reduktionsschritt wird eine Funktionsapplikation durch den Rumpf der entsprechenden Funktionsdefinitionen ersetzt. Dabei wird eine Substitution der Parameter vorgenommen. Ein Ausdruck ohne reduzierbare Teilausdrücke heißt Normalform. Die Normalform eines Ausdrucks ist somit das Resultat seiner Auswertung. verschiedene Auswertungsstrategien (left-most-outer-most)(Left-most-inner-most)(lazy evaluation) Hier rechne ich an der Tafel ein Beispiel: allEqual (3*(9- (negate 5))) 42 (square 6) ---------- ---------- ==> allEqual (3*(9- (-5))) 42 (6*6) ------- --- allEqual (3*(14))) 42 (36) ------ allEqual (42) 42 (36) ----------------------- (42==42) && (42==36) --------- ---------- True && False ------------------ False

Verschiedene Auswertungsstrategien Haskell verwendet eine Form der Left-most outermost Strategie Auf dieser Strategie baut die bedarfsgesteuerte Auswertungsstrategie auf (engl. lazy evaluation, call by need) Das Motto von Lazy Evaluation lautet: Leftmost outermost: Unter den am weitesten außen befindlichen Redexen, also reduzierbaren Teilausdrücken, die nicht Teilausdruck eines anderen Redexes sind, wird der am weitesten links stehende ausgewertet. Beispiel: inf x = inf x –-inf definiert die nirgendwo definierte Funktion const_fct y = 42 -konstante Funktion Wertet man in dem Ausdruck (const_fct (inf 0)) zunächst den inneren Redex (inf 0) aus, terminiert die Berechnung nicht. Wertet man zuerst den äußeren Redex aus, so erhält man sofort das Ergebnis 42 Nachteil der leftmost outermost-Strategie ist allerdings, dass eine effiziente Implementierung dieser Strategie schwieriger ist, da nun Funktionen mit unausgewerteten Argumentausdrücken aufgerufen werden können und somit beliebig unausgewertete Ausdrücke als „Daten“ verwaltet werden müssen. Berechne einen Ausdruck bzw. einen Teilausdruck nur, wenn es unbedingt nötig ist und dann auch nur einmal.

Rekursive Funktionen Rekursion ist eine selbst-referenzierende Art der Definition. Von Rekursion spricht man, wenn in der Definition einer Funktion auf die Funktion selbst Bezug genommen wird. Grundidee: Meistens „Divide and Conquer“ Prinzip Wir zerlegen das Problem in zwei Fälle: Einen leichten Fall, den wir lösen Und einen schweren Fall, den wir auf das leichte Problem zurückführen

Beispiele zur Rekursion Rekursion kommt häufig in der Mathematik und in der Informatik vor, z.B.: Die Fakultätsfunktion: n! Fibonacci-Zahlen: fib0=0 fib1=1 fibn=fibn-1+fibn-2 Übung: Programmieren Sie diese beiden Funktionen mit ihrem Tischnachbarn auf Papier und werten Sie einen nichttrivialen Beispielausdruck dazu aus. Abbruch-Bedingung immer über Bedingte Anweisung Wie sieht das hier bei dem Lambda-Kalkül aus, ist das da auch möglich? Es ist so (rekursiv) nicht möglich, da die Funktionen dort Anonyme Funktionen sind, keinen Namen haben. Gibt man den Funktionen namen, so wäre es möglich. Formuliert man das zu einer iterativen Formel, ist es möglich. Jede Rekursive Funktion lässt sich iterativ umschreiben

weiterführende Beispiele für Rekursion in Haskell Durch das Grundprinzip der deklarativen Sprachen in Kombination mit der Rekursion lassen sich somit leicht mathematische Strukturen auf den Computer übertragen. Als Beispiel kann man einen Baum nennen. Dieser lässt sich in Haskell rel. einfach als neue Datenstruktur anlegen und man kann sehr einfach auf ihm operieren. Einfache Beispiele sind z.B. folgende Geometrische Formen

Beispiele Zum Beispiel kann man Zettel 6 laden und zeigen, wie sich das Bild erstellt Der Vorteil von dem Grafikmodul ist, dass man schnell sieht, wenn man sich verprogrammiert hat und sogar relativ schnell nachvollziehen kann, wo der Fehler war. Das ist gut für Schüler, die Rekursion lernen sollen. Hugs verfügt auch über eine grafische Ausgabe. Mit ihr kann man relativ leicht solche Strukturen erzeugen.

Etwas komplexere fraktale Strukturen Mit etwas mehr Rechen-power und Mathematik ist es dann auch möglich beliebige komplexe Strukturen zu erzeugen. Weiteres Material (Tutorials, Papers, …) sowie den Compiler findet man unter www.haskell.org © by Christian Heil