Funktionale Programmierung mit Haskell



Ähnliche Dokumente
Funktionale Programmierung

Funktionale Programmierung mit Haskell

Typdeklarationen. Es gibt in Haskell bereits primitive Typen:

Fragen. f [ ] = [ ] f (x : y : ys) = x y : f ys f (x : xs) = f (x : x : xs) Wozu evaluiert f [1, 2, 3] (Abkürzung für f (1 : 2 : 3 : [ ]))?

Scala kann auch faul sein

Funktionale Programmierung ALP I. Funktionen höherer Ordnung. Teil 2 SS Prof. Dr. Margarita Esponda. Prof. Dr.

Kapitel 7 des Buches, von Java-Selbstbau nach Scala-Library portiert Christoph Knabe

CGI Programmierung mit Ha. Markus Schwarz

Javadoc. Programmiermethodik. Eva Zangerle Universität Innsbruck

Programmierkurs Java

Objektorientierte Programmierung für Anfänger am Beispiel PHP

Funktionale Programmierung mit Haskell

Java Einführung Operatoren Kapitel 2 und 3

Lineargleichungssysteme: Additions-/ Subtraktionsverfahren

4. Jeder Knoten hat höchstens zwei Kinder, ein linkes und ein rechtes.

Einführung in die Programmierung

Grundlagen der Programmierung 2. Bäume

Primzahlen und RSA-Verschlüsselung

II. Grundlagen der Programmierung. 9. Datenstrukturen. Daten zusammenfassen. In Java (Forts.): In Java:

Erweiterung der Aufgabe. Die Notenberechnung soll nicht nur für einen Schüler, sondern für bis zu 35 Schüler gehen:

Wintersemester Maschinenbau und Kunststofftechnik. Informatik. Tobias Wolf Seite 1 von 16

Fachdidaktik der Informatik Jörg Depner, Kathrin Gaißer

Der Aufruf von DM_in_Euro 1.40 sollte die Ausgabe 1.40 DM = Euro ergeben.

Programmieren in Haskell Einführung

Matrix42. Use Case - Sicherung und Rücksicherung persönlicher Einstellungen über Personal Backup. Version September

Übungen zu C++ Kapitel 1

Kapiteltests zum Leitprogramm Binäre Suchbäume

Reporting Services und SharePoint 2010 Teil 1

Java Kurs für Anfänger Einheit 5 Methoden

Formale Sprachen und Grammatiken

Binäre Bäume. 1. Allgemeines. 2. Funktionsweise. 2.1 Eintragen

5 DATEN Variablen. Variablen können beliebige Werte zugewiesen und im Gegensatz zu

Zeichen bei Zahlen entschlüsseln

Die Post hat eine Umfrage gemacht

Übung 9 - Lösungsvorschlag

Einrichtung des Cisco VPN Clients (IPSEC) in Windows7

Leichte-Sprache-Bilder

Informatik Grundlagen, WS04, Seminar 13

Programmieren in C. Macros, Funktionen und modulare Programmstruktur. Prof. Dr. Nikolaus Wulff

Anwenderleitfaden Citrix. Stand Februar 2008

.htaccess HOWTO. zum Schutz von Dateien und Verzeichnissen mittels Passwortabfrage

Einfache Ausdrücke Datentypen Rekursive funktionale Sprache Franz Wotawa Institut für Softwaretechnologie

Das erste Programm soll einen Text zum Bildschirm schicken. Es kann mit jedem beliebigen Texteditor erstellt werden.

Informatik 2 Labor 2 Programmieren in MATLAB Georg Richter

Programmiersprachen und Übersetzer

Algorithmen mit Python

ALP I. Funktionale Programmierung

Programmierparadigmen. Programmierparadigmen. Imperatives vs. objektorientiertes Programmieren. Programmierparadigmen. Agenda für heute, 4.

Würfelt man dabei je genau 10 - mal eine 1, 2, 3, 4, 5 und 6, so beträgt die Anzahl. der verschiedenen Reihenfolgen, in denen man dies tun kann, 60!.

4. AUSSAGENLOGIK: SYNTAX. Der Unterschied zwischen Objektsprache und Metasprache lässt sich folgendermaßen charakterisieren:

Web-Kürzel. Krishna Tateneni Yves Arrouye Deutsche Übersetzung: Stefan Winter

Folgende Einstellungen sind notwendig, damit die Kommunikation zwischen Server und Client funktioniert:

Excel Funktionen durch eigene Funktionen erweitern.

2. Programmierung in C

Adressen. Praktikum Funktionale Programmierung Organisation und Überblick. Termine. Studienleistung

Motivation. Formale Grundlagen der Informatik 1 Kapitel 5 Kontextfreie Sprachen. Informales Beispiel. Informales Beispiel.

OP-LOG

Grundlagen von Python

Professionelle Seminare im Bereich MS-Office

Algorithmik II. a) Fügen Sie in einen anfangs leeren binären Baum die Schlüsselfolge 20, 28, 35, 31, 9, 4, 13, 17, 37, 25 ein.

Jeopardy and andere Quizformate im bilingualen Sachfachunterricht Tipps zur Erstellung mit Powerpoint

Anleitung über den Umgang mit Schildern

AUTOMATISCHE -ARCHIVIERUNG. 10/07/28 BMD Systemhaus GmbH, Steyr Vervielfältigung bedarf der ausdrücklichen Genehmigung durch BMD!

AGROPLUS Buchhaltung. Daten-Server und Sicherheitskopie. Version vom b

Überprüfung der digital signierten E-Rechnung

Einführung in die Programmierung Laborübung bei Korcan Y. Kirkici. 12.Übung bis

Abamsoft Finos im Zusammenspiel mit shop to date von DATA BECKER

Windows 7: Neue Funktionen im praktischen Einsatz - Die neue Taskleiste nutzen

Datenbank-Verschlüsselung mit DbDefence und Webanwendungen.

Was meinen die Leute eigentlich mit: Grexit?

TECHNISCHE UNIVERSITÄT MÜNCHEN FAKULTÄT FÜR INFORMATIK

Client-Server-Beziehungen

Informatik 12 Datenbanken SQL-Einführung

Das neue Volume-Flag S (Scannen erforderlich)

Die Gleichung A x = a hat für A 0 die eindeutig bestimmte Lösung. Für A=0 und a 0 existiert keine Lösung.

FORUM HANDREICHUNG (STAND: AUGUST 2013)

SEP 114. Design by Contract

Der lokale und verteilte Fall

Viele Bilder auf der FA-Homepage

Stellen Sie bitte den Cursor in die Spalte B2 und rufen die Funktion Sverweis auf. Es öffnet sich folgendes Dialogfenster

e-books aus der EBL-Datenbank

Computerarithmetik ( )

Wurzeln als Potenzen mit gebrochenen Exponenten. Vorkurs, Mathematik

10.6 Programmier-Exits für Workitems

Die Beschreibung bezieht sich auf die Version Dreamweaver 4.0. In der Version MX ist die Sitedefinition leicht geändert worden.

Alle gehören dazu. Vorwort

3. LINEARE GLEICHUNGSSYSTEME

Testklausur 1 zur Vorlesung. Modellierung und Programmierung I. Dr. Monika Meiler Zeit: 60 Minuten

Stundenerfassung Version 1.8 Anleitung Arbeiten mit Replikaten

Installation der SAS Foundation Software auf Windows

Inhalt. 1 Einleitung AUTOMATISCHE DATENSICHERUNG AUF EINEN CLOUDSPEICHER

Grammatiken. Einführung

Das Typsystem von Scala. L. Piepmeyer: Funktionale Programmierung - Das Typsystem von Scala

Java Kurs für Anfänger Einheit 4 Klassen und Objekte

Einführung in die objektorientierte Programmierung mit Java. Klausur am 19. Oktober 2005

SEMINAR Modifikation für die Nutzung des Community Builders

P&P Software - Adressexport an Outlook 05/29/16 14:44:26

Erstellen eines Screenshot

Transkript:

Funktionale Programmierung mit Haskell Prof. Dr. Hans J. Schneider Lehrstuhl für Programmiersprachen und Programmiermethodik Friedrich-Alexander-Universität Erlangen-Nürnberg Sommersemester 2012 I. Die Sprache Haskell 1. Einführung in Haskell 2. Funktionen als Basis der Programmierung 3. Algebraische Typen 4. Grundlagen des Typsystems 5. Strukturierung durch Typklassen 6. Verbergen von Information 7. Sprachergänzungen II. Fallstudien c Hans J. Schneider 2012

Literate programming Literate Programming kombiniert Programmcode und ausführliche Dokumentation. Programmcode beginnt jeweils mit der ersten Zeile nach einer Zeile, die mit \begin{code} beginnt. Programmcode endet jeweils unmittelbar vor der Zeile, die mit \end{code} beginnt. Alle anderen Zeilen werden vom Haskell-Interpretierer (Compiler) nicht beachtet. Diese anderen Zeilen können beispielsweise LaTeX-Zeilen sein. Der Bericht über die Implementierung der Graphtransformationen mit Haskell (siehe Internetseite zu den Graphtransformationen) ist so geschrieben. Bei Bedarf kann die code-umgebung durch Kopieren der verbatim- Umgebung definiert werden. Funktionale Programmierung mit Haskell 7.1

Übersicht Haskell wertet Parameter erst aus, wenn sie benötigt werden, und nur soweit, wie sie benötigt werden (Lazy evaluation). Dies ermöglicht (potentiell) unendlich lange Listen. Es kann sinnvoll sein, aus Effizienzgründen ausdrücklich die strikte Auswertung zu verwenden. Lazy evaluation ist auch beim Mustervergleich möglich: lazy patterns. Die Unifikation wird nur teilweise durchgeführt. Einige Anmerkungen zur Ein- und Ausgabe. Funktionale Programmierung mit Haskell 7.2

Unendliche Listen Die rekursive Implementierung der Fibonacci-Zahlen ist sehr aufwendig: fib 0 = 1 fib 1 = 1 fib n = fib (n-2) + fib (n-1) Direkter Zugriff auf die Liste bereits berechneter Zahlen: fib 0 = 1 fib 1 = 1 fib n = flist!!(n-1) + flist!!(n-2) where flist = map fib [0..] map fib ordnet jeder Komponenten einer Liste ihre Fibonacci-Zahl zu. [0..] bezeichnet die unendliche Liste [0,1,2,3,4,...] Unendliche Listen sind möglich, weil Haskell Parameter erst und soweit auswertet, wie sie wirklich benötigt werden. Funktionale Programmierung mit Haskell 7.3

Sieb des Erathostenes Man braucht nicht zu wissen, wie lang eine Liste wird! Sieb des Erathostenes: primes = sieve([2..]) where sieve(p:x) = p : sieve([n n<-x, n mod p > 0]) Auch auf unendliche Listen können alle Listenoperationen angewandt werden: prime_number(i) = primes!! (i-1) Main> prime_number 150 863 Main> initial(15, primes) [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47] Funktionale Programmierung mit Haskell 7.4

Grenzwerte Grenzwerte sind ebenfalls ein Beispiel, wo man nicht im vorhinein weiß, wie weit die Liste geht. Definition: limes(a:b:x) = if a == b then a else limes(b:x) limes(other) = error "incorrect use of limes" Bemerkung: other ist ein beliebig gewählter Bezeichner. Beispiel: f(x) = x 2 z = 0 Newtonsche Iteration: f(x i+1 ) = x i f(x i) f (x i ) = 1 ) 2 (x i + zxi wurzel z = limes (iterate f 1) where f(x) = (x + z/x)/2.0 Main> wurzel 3 1.73205080756888 Da bei Gleitpunktzahlen Rundungsfehler auftreten, ist der Gleichheitstest etwas gefährlich. (siehe Übung) Funktionale Programmierung mit Haskell 7.5

Funktionen als Objekte Funktionen sind Objekte wie alle anderen und können entsprechend verwendet werden, z.b. als Operanden spezieller Funktionen oder als Komponenten in Datenstrukturen. Komposition von Funktionen: f1 x = 2*x f2 x = x+2 f3 = f1. f2 Liste von Funktionen: pow 0 n = 1 pow i n = n * (pow (i-1) n) powers = [ pow i i <- [0..] ] In der angewandten Mathematik spielen Folgen von Funktionen etwa bei der Approximation eine Rolle. Funktionale Programmierung mit Haskell 7.6

Funktoren I Wir kennen bereits Funktionen höherer Ordnung, die Funktionen auf alle Elemente einer Liste anwenden, z.b.: map f [] = [] map f (x:xs) = f x : map f xs Im Standardvorspann definiert: class Functor s where fmap :: (a -> b) -> s a -> s b Damit können wir eine Funktion auf die Elemente einer beliebigen Struktur anwenden, z.b. eines binären Baumes: instance Functor StandBinTree where fmap f EmptyBinTree = EmptyBinTree fmap f (Node(x, l, r)) = Node(f x, (fmap f l), (fmap f r)) Main> t2 (87 (123 (471) (200)) (256 () (317))) Main> fmap (+1) t2 (88 (124 (472) (201)) (257 () (318))) Funktionale Programmierung mit Haskell 7.7

Funktoren II fmap wendet eine Funktion auf alle Elemente einer Struktur an. class Functor s where fmap :: (a -> b) -> s a -> s b Bei rekursiv aufgebauten Strukturen macht es auch Sinn, eine Funktion auf Teilstrukturen anzuwenden. class RecFunctor s where rmap :: (s a -> b) -> s a -> s b instance RecFunctor StandBinTree where rmap f EmptyBinTree = EmptyBinTree rmap f (Node(x, l, r)) = Node(f (Node(x,l,r)), (rmap f l), (rmap f r)) Main> t2 (87 (123 (471) (200)) (256 () (317))) Main> rmap height t2 (3 (2 (1) (1)) (2 () (1))) Main> rmap balance t2 (0 (0 (0) (0)) (1 () (0))) Funktionale Programmierung mit Haskell 7.8

Lazy Patterns: Beispiel Struktur eines Client-Server-Systems: requests Client Server responses initialize Der Server verarbeitet die Anfragen des Client und liefert für jede Anfrage eine Antwort: server (req:reqs) = process req : server reqs Der Client konstruiert aus dem Anfangswert und den Antworten des Servers eine Ergebnisliste: client initialize ~(resp:resps) = initialize : client (next resp) resps ~(resp:resps) verhindert die Auswertung von resps. next bearbeitet die Antwort vor der Weitergabe. Quelle: A Gentle Introduction to Haskell 98 Funktionale Programmierung mit Haskell 7.9

Lazy Patterns: Beispiel (II) Struktur eines Client-Server-Systems: requests Client Server responses initialize Einfaches Beispiel einer Verarbeitung: initialize = 0 next resp = resp process req = req + 1 Kommunikation der Komponenten: requests = client initialize responses responses = server requests Beispiellauf: Main> take 10 requests [0,1,2,3,4,5,6,7,8,9] Funktionale Programmierung mit Haskell 7.10

Lazy Patterns: Semantik A lazy pattern has the form ~pat. Lazy patterns are irrefutable: matching a value v against ~pat always succeeds. If an identifier in pat is later used on the right-hand side, it will be bound to that portion of the value that would result if v were to successfully match pat, and otherwise. Beispiel: Aufruf: client initialize responses Definition: client initialize ~(resp:resps) = initialize : client (next resp) resps Ohne ~ ergibt sich in unserem Beispiel. Funktionale Programmierung mit Haskell 7.11

Strikte Parameterauswertung Die Funktion seq erreicht, dass ein Parameter stets ausgewertet wird. Ihre Definition entspricht: seq b = seq a b = b, if a Beispiel: take :: Integer -> [Integer] -> [Integer] take 0 _ = [] take _ [] = [] take n (x:xs) = x : take (n-1) xs bot = bot take 0 bot funktioniert. seq (take 0) bot funktioniert nicht! Funktionale Programmierung mit Haskell 7.12

Die Standardklasse Show The Show class is used to convert values to strings (Report App. A.2). class Show a where showsprec :: Int -> a -> ShowS show :: a -> String showlist :: [a] -> ShowS Beschreibung der Funktionen: showsprec and showlist return a String-to-String function, to allow constant-time concatenation of its results using function composition. A specialised variant, show, is also provided, which uses precedence context zero, and returns an ordinary String. The method showlist is provided to allow the programmer to give a specialised way of showing lists of values. Aus der Betrachtung der vordefinierten Klassen kann man einiges lernen. Funktionale Programmierung mit Haskell 7.13

Default-Methoden in der Standardklasse Show ShowS ist ein Funktionstyp: type ShowS = String -> String Die Klasse Show: class Show a where showsprec :: Int -> a -> ShowS show :: a -> String showlist :: [a] -> ShowS showsprec _ x s = show x ++ s show x = showsprec 0 x "" Es genügt, entweder show zu definieren oder showsprec. Weiter gibt es: shows = showsprec 0 Funktionale Programmierung mit Haskell 7.14

Beispiele zu ShowS showchar und showstring sind Curry-Funktionen: type ShowS = String -> String showchar showstring :: Char -> ShowS :: String -> ShowS showchar a ist eine Funktion, die das Zeichen a an den Anfang einer Zeichenkette stellt: showchar = (:) Main> showchar a "bcd" "abcd" Entsprechend setzt showstring eine Zeichenkette voran: showstring = (++) Main> showstring "ab" "cde" "abcde" Funktionale Programmierung mit Haskell 7.15

Traditionelle Implementierung von showlist Ohne Curry-Funktion würde man das folgendermaßen machen: showlist :: (Show a) => [a] -> String showlist [] = "[]" showlist (x:xs) = "[" ++ show x ++ showl xs where showl [] = "]" showl (x:xs) = "," ++ show x ++ showl xs Die Auflösung der Rekursion führt zu fortgesetzten Konkatenationsoperationen: showlist [1,2,3] = "[" ++ show 1 ++ showl [2,3] = "[1" ++ "," ++ show 2 ++ showl [3] = "[1,2" ++ "," ++ show 3 ++ showl [] = "[1,2,3" ++ "]" = "[1,2,3]" und damit zu quadratischer Laufzeit! (Aus Gründen der Übersichtlichkeit sind die weiter links stehenden Konkatenationsoperationen zusammengefasst, obwohl ++ rechtsassoziativ ist.) Funktionale Programmierung mit Haskell 7.16

Standardimplementierung von showlist Implementierung im Standardvorspann: showlist :: [a] -> (String -> String) showlist [] = showstring "[]" showlist (x:xs) = showchar [. shows x. showl xs where showl [] = showchar ] showl (x:xs) = showchar,. shows x. showl xs Arbeitsweise der Standardimplementierung: showlist [1,2,3] "" = showchar [. shows 1. showl [2,3] "" = showchar [. shows 1. showchar,. shows 2. showl [3] "" = showchar [. shows 1. showchar,. shows 2. showchar,. shows 3. showl [] "" Da die Funktionskomposition rechtsassoziativ ist, läuft das auf eine fortgesetzte Anwendung des cons-operators hinaus! Funktionale Programmierung mit Haskell 7.17

Monadische Klassen Die erste Begegnung mit monadischen Klassen ist schwierig, aber das IO-System von Haskell basiert darauf. Monad An entity or elementary being thought of as a microcosmos or ultimate unit (Webster). Mathematically, monads are governed by set of laws that should hold for the monadic operations (A gentle introduction...). Functor (haben wir schon gesehen) Die Klasse Functor definiert nur eine einzige Funktion: fmap. fmap wendet eine Operation auf alle Elemente innerhalb eines Containers an und liefert einen Container von gleicher Gestalt zurück. Gesetze für fmap: fmap id = id fmap (f. g) = fmap f. fmap g Funktionale Programmierung mit Haskell 7.18

Klasse Monad Definition im Vorspann: class Monad m where (>>=) :: m a -> (a -> m b) -> m b (>>) :: m a -> m b -> m b return :: a -> m a fail :: String -> m a return fügt einen Wert vom Typ a in eine Monade über a ein. >> verknüpft zwei Monaden, bei >>= ist die Verknüpfung parametrisiert. m >> k = m >>= \_ -> k fail s = error s Die Listen sind eine Instanz von Monad: (>>=):: [a] -> (a -> [b]) -> [b] m >>= k = concat (map k m) Main> "abc" >>= (\x -> [succ x]) "bcd" Main> zip [1,2,3] [4,5,6] >>= (\(x,y) -> [x*y]) [4,10,18] Funktionale Programmierung mit Haskell 7.19

Do-Ausdrücke (I) Do-Ausdrücke sind eine suggestive Syntax für Ketten monadischer Operationen. Beispiel: Main> zip [1,2,3] [4,5,6] >>= (\(x,y) -> [x*y]) [4,10,18] Main> do {(x,y) <- zip [1,2,3] [4,5,6]; [x*y] } [4,10,18] Den Linkspfeil <- kennen wir von den Zermelo-Fraenkel-Ausdrücken: Main> [ x*y (x,y) <- zip [1,2,3] [4,5,6] ] [4,10,18] Definition der do-ausdrücke: do {e} = e do {e;stmts} = e >> do {stmts} do {p <- e; stmts} = let ok p = do {stmts} ok _ = fail "..." in e >>= ok ok ist eine (von p abhängige) Funktion. Funktionale Programmierung mit Haskell 7.20

Do-Ausdrücke (II) Beispiel für >>: Main> do {putstr "abc"; putstr "\n"} abc Main> putstr "abc" >> putstr "\n" abc Beispiel aus dem Standardvorspann: getline :: IO String getline = do c <- getchar if c == \n then return "" else do s <- getline return (c:s) getchar :: IO Char -- primitive Funktion Die IO-Funktionen im Standardvorspann sind in diesem Stil definiert. Funktionale Programmierung mit Haskell 7.21

Anhang: Gesetze für Monaden Instances of Monad should satisfy the following laws: return a >>= k = k a m >>= return = m m >>= (\x -> k x >>= h) = (m >>= k) >>= h (Assoziativgesetz) Instances of both Monad and Functor should additionally satisfy the law: fmap f xs = xs >>= return. f Beispiel: Listen f e = f(e) (return. f) e = [f(e)] xs >>= return. f = [f(e) e <- xs ] = map f xs Funktionale Programmierung mit Haskell 7.22

Anhang: Die Standardklasse Read The Read class is used to convert values from strings (Report 7.1 / A.2): class Read a where readsprec :: Int -> ReadS a readlist :: ReadS [a] Die Standardtypen sind Instanzen von Read, z.b. instance Read Float where readsprec p = readfloat Main> :info readfloat readfloat :: RealFrac a => ReadS a Anmerkung: RealFrac umfasst standardmäßig Float und Double. Unbequem: type ReadS a = String -> [(a,string)] Main> readfloat "1.6 3.6 7.8" [(1.6," 3.6 7.8")] Am bequemsten ist es, Beispiele in den Dateien selbst zu speichern. Funktionale Programmierung mit Haskell 7.23

Anhang: Hilfsfunktionen für Read Funktion, die eine Eingabezeichenkette in eine Liste von Gleitpunktzahlen umwandelt: Main> readfloatlist "1.6 3.4 9.0" [1.6,3.4,9.0] Definition: readfloatlist "" = [] :: [Float] readfloatlist s = head:(readfloatlist rest) where [(head, rest)] = readfloat (removeblanks s) removeblanks [] = [] removeblanks ( :rest) = removeblanks rest removeblanks other = other Der Datentyp ReadS erlaubt jedoch die Kombination verschiedener Typen in der Eingabezeichenkette. Vorsicht: In den neueren Hugs-Versionen (ab März 2005) sind Funktionen wie beispielsweise readfloat nicht mehr automatisch verfügbar. Lösung: import Numeric Funktionale Programmierung mit Haskell 7.24