Informatik Institut, Fachbereich Mathematik und Informatik ProInformatik: Funktionale Programmierung Woche 2: 4.-8.8.2008 Dozentin: Maria Knobelsdorf Tutorin, Tutor: Peggy Sabri, Dirk Wiesenthal 28.7 – 22.8.2008 Raum 005, Takustr. 9 14195 Berlin Tag 14, ILOs • • • • • • • Wissen: Wiederholung des λ-Kalküls α-Konversion Typklassen in Haskell Anwenden: Reduzieren von λ-Ausdrücken Typklassen in Haskell definieren ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 2 λ-Kalkül – eine kurze Wiederholung • Wozu λ-Kalkül? Ursprüngliches Ziel war eine Definition für Funktionen zu finden, die eine extrem einfache Syntax hat. • Das ganze wurde benötigt, um besser zu definieren was Berechenbarkeit ist, bzw. was berechenbare Funktionen sind. (Mehr zu Berechenbarkeit in GTI, Grundlagen der Theoretischen Informatik, im Sommersemester) • Das λ-Kalkül wurde jedoch auch die Grundlage für Funktionale Programmiersprachen ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 3 Die Syntax des λ-Kalküls Ein λ-Ausdruck A ist syntaktisch in BNF definiert als: <λ-Ausdruck> := <Variable>|<Abstraktion>|<Application> <Variable> := a|b|…z|f|… --eine abzählbar unendliche Menge an Variablen <Abstraktion> := λ<Variable>.<λ-Ausdruck> <Application> := (<λ-Ausdruck>) <λ-Ausdruck> ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 4 Die Syntax des λ-Kalküls • Im λ-Kalkül gibt es nur Variablen, Abstraktionen und Applicationen • Es gibt keine Zahlen, Wahrheitswerte, keine Typen • Funktionen haben keinen Namen sondern werden anonymisiert als Abstraktion oder als Variable dargestellt ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 5 Konventionen • Die Abstraktion ist rechtsassoziativ: λx. λy.λz.(x) y z (λx. (λy.(λz.(x) y z))) • Warum? Wegen der Definition von <Abstraktion>: Alles rechts vom Punkt ist ein weiterer <λ-Ausdruck> (λx. λy.λz.(x)y) z (λx.<λ-Ausdruck>)z ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 6 Daher kann man Abstraktion und Application auch definieren als: <Abstraktion> := (λ<Variable>.<λ-Ausdruck>) <Application> := ((<λ-Ausdruck>)<λ-Ausdruck>) • Wodurch die Rechtsassoziativität sichtbar gemacht wird • Um Klammern zu sparen werden dann bei der Application wegen Linksassoziativität die inneren Klammern weggelassen: • <Application> (x y z) (((x) y) z) ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 7 β-Reduktion: Auswerten von λ-Ausdrücken • λ-Ausdrücke werden β-Redex genannt, wenn sie die Form haben: (λ<Variable>.<λ-Ausdruck>) <λ-Ausdruck> • Ein λ-Ausdruck in Redexform kann ausgewertet werden, indem mit der sog. β-Reduktion eine Termersetzung ausgeführt wird: Sei x =<Variable> und E,Q = <λ-Ausdruck>, die β-Reduktion ist definiert als: (λx.E)Q ->β E[x/Q] := λ-Ausdruck, der aus E entsteht durch Ersetzen aller freien Variablen x in E durch Q • Ein λ-Ausdruck wird Normalform genannt, wenn er keine β-Redexe mehr enthält. ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 8 Beispiele (λx.x) y ->β y (λx.y) z ->β y (λx. λy.(x) y) a ->β λy.(a) y (((λx. λy.λz.(x) (y) z)a)b)c ->β (a)(b)c ((λx. λy.(x) y) y)b ->β (y) b ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 9 α-Konversion • Die α-Konversion erlaubt gebundene Variablen umzubennen, um Konflikte zu vermeiden: Sei x =<Variable> und E,Q = <λ-Ausdruck>, die α-Konversion ist definiert als: (λx.E)Q ->α (λy.E‘)Q, wobei alle freien Vorkommen von x in E durch y ersetzt werden ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 10 Auswertungsreihenfolge Wo soll man anfangen? • Man fängt wie beim Syntaxbaum mit dem gesamten <λ-Ausdruck> an und sucht den ersten β-Redex und wertet ihn aus und macht dann weiter. (Das ist eine Festlegung!) Da per Definition die <Abstraktion> rechtsassoziativ und die <Application> linksassoziativ sind, entsteht dadurch automatisch die Reihenfolge: Soweit wie möglich links und außen anfangen: ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 11 (((λx. λy.λz.(x) (y) z)a)b)c (<λ-Ausdruck>) c ((<λ-Ausdruck>))b)c (((<λ-Ausdruck>))a)b)c ((( ((( λx. λy. λz. (x)(y) z λx. (λy.(λz.(x)(y) z)) )a)b)c )a)b)c => Links und außen angefangen und vorgearbeitet zum ersten β-Redex (λx. < λ-Ausdruck>) a ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 12 Beispiele (λx.λy.λz.(x)(y)z) a ->β ? λx.(λy.y)x ->β ? ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 13 Church-Rosser Theorem • Was wenn man anders vorgeht und dadurch verschiedene Auswertungsreihenfolgen möglich sind? Theorem von Church-Rosser • Sei E ein β-Redex und M und N verschiedene Ergebnisse von Auswertungsreihenfolgen der βReduktion mit: E ->β* M und E ->β* N, dann gibt es ein Z, so dass M ->β* Z und N ->β* Z ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 14 Left-most, Outer-most • Die Festlegung: soweit wie möglich links und außen anfangen ist also korrekt • Ist das auch gut? Im Sinne von optimal? • Wie ist das in Haskell? • β-Reduktion und Lazy Evaluation Exakte Auswertung eines Haskell-Ausdrucks -> Master-Veranstaltung: „Fortgeschrittene Aspekte der Funktionalen Programmierung“, bei E. Fehr, im Sommer-Semester! ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 15 Typklassen in Haskell • Welche Signatur hat elemA? elemA e [] = False elemA e (x:xs) |e==x = True |otherwise = elemA e xs elemA :: a -> [a] -> Bool Wobei a Elemente sind, die man auf Gleichheit überprüfen kann, also alle Datentypen, die den logischen Vergleich (==) auf ihren Werten zulassen ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 16 Typklasse in Haskell • In Haskell wird eine Menge von Datentypen, die für bestimmte Funktionen definiert sind, Typklasse genannt: class Eq a where (==)::a->a->Bool elemA :: Eq a => a -> [a] -> Bool ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 17 Beispiel allEqual :: Int -> Int -> Int -> Bool allEqual x y z = (x==y) && (y==z) Kann verallgemeinert werden zu: allEqual :: Eq a=> a -> a -> a -> Bool ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 18 Typklassen: Definition class <Klassenname> <Variable x> where …Signaturen von Funktionen, die x verwenden… Bsp. class Visible a where toString :: a -> String size :: a -> Int • Zu beachten: in der Typklasse sind nur Signaturen angegeben, nicht die Definitionen der Funktion ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 19 Instances of a type class • Um einen Datentyp als Ausprägung einer Typklasse zu definieren wird eine sog. Instance definiert: instance <Typklasse T> <Datentyp D> where Funktionsdefinitionen der in T definierten Funktionen mit D • Typklassen und instances verhalten sich wie in objektorientierten Sprachen Interfaces und Klassen, die diese implementieren! ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 20 Bsp. Bsp. class Visible a where toString :: a -> String size :: a -> Int instance Visible Bool where toString True = “True“ toString False = “False“ size True = 1 size False = 0 ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 21 Überladene Funktionen instance Visible Int where toString x = (horner x):““ size x = x horner z = … instance Visible Char where toString c = c:““ size c = 1 Die Methoden einer Typklasse werden ab zwei Instances überladen, d.h. mehrmals definiert für verschiedene Datentypen ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 22 Default functions • Innerhalb der Typklassendefinition können Funktionen auch definiert werden • Diese default Funktionen können nachher in den instances überschrieben werden: class Eq a where (==), (/=)::a->a->Bool x /= y = not (x==y) x == y = not (x/=y) • In dem Fall kann man als Analogie zu objektorientierten Sprachen das Ganze als abstrakte Klasse vorstellen. ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 23 Beispiele func::Eq a =>(a,a)->(a,a) func (a,b) = (a==b,b) ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 24 Zusammengesetzte instances instance (Eq a) => Eq [a] where (==) [] [] = True (==) [] (x:xs) = False (==) (x:ys) [] = False (==) (x:xs) (y:ys) = x == y && xs == ys • Listen vom Typ a können nur dann mit (==) verglichen werden, wenn das für Werte von a gilt. • Implementierung gibt an, wie das geschieht. ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 25 Typklassen aus Typklassen definieren • Typklassen können voneinander erben: Bsp. Die Typklasse Ord: class Eq a => Ord a where (>), (<=), (>), (>=)::a->a->Bool • Jede instance von Ord muss nicht nur die Funktionen von Ord, sondern auch von Eq definieren. => Vollständige Definition aller Typklassen und instances in der Haskell Bibliothek. -> Prelude.hs ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 26 Typklasse Ord Was ist Ordering, LT, EQ und GT? Antwort: Ordering ist ein algebraischer Datentyp mit: data Ordering = LT | EQ | GT Mehr dazu morgen! ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 27 Weitere Klassen Enum ermöglicht Listen mit .. Zu definieren: [ [ [ [ n .. ] = enumFrom n – Pseudocode n, m .. ] = enumFromThen n m n .. m ] = enumFromTo n m n, n' .. m ] = enumFromThenTo n n' m ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 28 Num erbt von Eq und Show und definiert die Funktionen (*), (+), (-): class (Eq a, Show a) => Num a where (+) :: a -> a -> a (-) :: a -> a -> a (*) :: a -> a -> a instance Num Int where x+y = primAdd x y – systemdefiniert ... class Num a => Fractional a where (/) :: a -> a -> a ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 29 Mehrere Oberklassen • Ganze Zahlen sind aufzählbar (Enum) und numerisch (Num). Integral Subklasse von Enum und Num class (Enum a, Num a) => Integral a where quot, rem, div, mod :: a -> a -> a quotRem, divMod :: a -> a -> (a,a) even, odd :: a -> Bool toInteger :: a -> Integer -- toInt :: a -> Int ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 30 Typherleitung 1. Überprüfen, welche Parameter es gibt 2. Für jeden Parameter analysieren, was in der Funktion mit ihm geschieht 3. Aus 2. ableiten, welchen Typ jeder Parameter mindestens haben muss, damit die 4. Funktionsauswertung durchgeführt werden kann 5. Die analysierten Typen aus 3. zusammenfassen zu einem Obertyp 6. Signatur bestimmen ProInformatik, Funktionale Programmierung, 28.7-22.8.2008, M. Knobelsdorf 31