Das Funktionale Quiz Funktionale Programmierung Was weiß man über eine Funktion mit Signatur a -> Int 10.5.2005 Die Funktion ist nicht-strikt 5-6 Das Funktionale Quiz Das Funktionale Quiz Ist es möglich, eine Funktion mit dem Typ Int -> a zu schreiben Nein, denn a müssen Parameter sein, der nicht verwendet wird, Ergebnisausdruck hat irgendeinen Typ Ist ++ ein Konstruktor für Listen? Nein, Aufbau z.B. von [1,2,3] nicht eindeutig: [1,2,3] = [1] ++ [2,3] = [1,2] ++ [3] Ja: returnA :: Int -> a returnA x = error "ein a" 7-8 Abstraktion über das Muster Themen heute Muster: • Für [] liefere [] • Sonst, wende beliebige Funktion auf erstes Element an und hänge das Ergebnis vor rekursiven Aufruf • Higher-Order Funktionen über Listen • Überladung • Abstraktion darüber: map map :: (a -> b) -> [a] -> [b] map f [] = [] map f (x:xs) = f x : map f xs • Algebraische Datentypen Beob: Funktionen müssen Werte sein 11 Filtern als Muster Higher-Order Funktionen über Listen Filtere die Elemente heraus, die Prädikat nicht erfüllen filter :: (a -> filter p [] filter p (x:xs) | p x | otherwise • Ziel: Über Berechnugsmuster abstrahieren Bool) -> [a] -> [a] = [] • Erstes Muster: Listen homomorph abbilden Beispiel: = x : filter p xs = filter p xs doubleAll :: [Int] -> [Int] doubleAll [] = [] doubleAll (x:xs) = 2*x : doubleAll xs signAll :: [Int] -> [Bool] signAll [] = [] signAll (x:xs) = (x >= 0) : signAll xs 12 Vorteile von foldr Abstrahieren über primitive Rekursion • Wiederverwendung des Musters Schablone für p.R. war: f [] = ... f (x:xs) = ... x ... (f xs) ... x ... • Primitive Rekursion wird offensichtlich Abstraktion darüber braucht: • In Beweisen können Eigenschaften von foldr verwendet werden • Ausdruck für [] • Ausdruck, der aus x und f xs den Wert für f (x:xs) berechnet 15 Funktionen als Ergebnisse Abstrahieren über primitive Rekursion • Schon mehrfach vorgekommen: curry, (.),twice foldr :: (a -> b -> b) -> b -> [a] -> b foldr cons nil [] = nil foldr cons nil (x:xs) = cons x (foldr cons nil xs) • Außerdem eigentlich ständig, da Funktionen nur einstellig: f 1 2 ist ja eigentlich (f 1) 2 • Explizt: Über lokale Funktionsdefinition: addSubstNum :: Int -> ((Int -> Int), (Int -> Int)) addSubstNum n = (addN, substN) where addN m = n+m substN o = n-o 16 • nil ist der Wert für [] • cons berechnet aus erstem Element und Funktionswert für xs den Funktionswert für x:xs sumList :: [Int] -> Int sumList = foldr (+) 0 Beweise für Higher-Order-Funktionen Lambda-Notation: Funktion als Ausdruck • Ist ein Parameter eine Funktion, dann durchreichen • Der Ausdruck \x -> e wertet zu einer Funktion aus, die x als Parameter und e als Rumpf hat • Prinzip der Erweiterbarkeit: Zwei Funktionen sind gleich, wenn sie für alle Argumente die gleichen Resultate ergeben. Z.Z.: f . id = f • Damit addSubstN: addSubstNum n = ((\m -> n+m), (\o -> n-o)) (f . id) x = f (id x) = f x • Auch mit mehreren Parametern: add = \x y -> x + y 19 Beispielbeweis Allgemeine Syntax für Lambda-Notation Z.Z. map f . rev = rev . map f Induktion über Liste Induktionsanfang: Trivial Def. rev ÜA Linke Seite: map f (rev x:xs) = map f (rev xs++[x]) = map f Def. map IA (rev xs) ++ map f [x] = map f (rev xs) ++ [f x] = rev (map f xs) ++ [f x] Rechte Seite: rev (map f (x:xs)) rev (map f xs ++ [f x] Def map Dev rev = rev (f x : map f xs) = 20 \ pat1 ... patN -> e "Anonyme Funktion" Lambda-Notation ist Teil des Haskell-Kerns, f x ... = e wird übersetzt nach f = \x ... -> e Typklassen Weitere Theoreme • class Eq a where (==) :: a -> a -> Bool map (f . g) xs = (map f . map g) xs (filter p . map f) xs = (map f . filter (p . f)) xs map f (reverse xs) = reverse (map f xs) map f (ys++xs) = map f ys ++ map f xs • Damit: elem :: Eq a => a -> [a] -> Bool • Eq a ist der Kontext 23 Typklassen definieren Überladung • class C ty where idecls • Beob: Manche Funktionen funktionieren nur auf bestimmten Typen • Abgeleitete Klassen: class scontext => C ty where idecls • Bsp: Element-Check für Listen braucht Gleichheit auf Listenelementen • C: Name der Klasse • ty: Typvariable (nur eine!) 1. Möglichkeit: Gleichheit mit übergeben: elem :: a -> [a] -> (a -> a -> Bool) -> Bool • idecls sind Typsignaturen der Operationen oder Defaultdefinitionen 2. Möglichkeit: Typ der Elemente einschränken elem :: a -> [a] -> Bool, a hat Gleichheit • Superklassen: scontext -> simpleclass | ( simpleclass1 , ... , simpleclassn ) simpleclass -> C ty • a muss zu einer bestimmten Klasse Typen gehören: Typklasse 24 Instanzdeklarationen Beispiel • Fügen einen Typ einer Typklasse hinzu • Geben die Definitionen der überladenen Funktionen an • Syntax: instance Class Type [where decls] class Eq a => Ord a where (<),(<=),(>),(>=) :: a -> a -> Bool • Beispiel: instance Eq Bool where True == True = True False == False = True _ == _ = False 27 Instanzdeklaration mit Kontext Defaultdefinitionen • Bei der Instanzdeklaration Typklassen voraussetzen • Geben Implementierung an, können "überschrieben" werden • Syntax: instance [scontext =>] Class Type [where idecls] • Beispiel: instance [] == x:xs == _ == • Häufig wechselseitig rekursiv class Eq a [] y:ys _ => Eq [a] where = True = x == y && xs == ys = False 28 Eq a where (==), (/=) :: a -> a -> Bool x /= y = not (x == y) x == y = not (x /= y) Produkt-Typen Eingebaute Typklassen • Typen mit Gleichheit: Eq s.o. • Konstruktoren haben Argumente • Typen mit totaler Ordnung: class (Eq a) => Ord a where compare :: a -> a -> Ordering (<), (<=), (>=), (>) :: a -> a -> Bool max, min :: a -> a -> a • data People = Person String Int | DeadPerson String Int Int • Dann ist Person :: String -> Int -> People • Typen die angezeigt werden können class Show a where show :: a -> String • Funktion darauf: instance Show People where show (Person name born) = name ++ " * " ++ show born show (DeadPerson name born died) = name ++ " * " ++ show born ++ " + " ++ show died 31 Allgemeine Syntax • Aufzählungstypen class Enum a where succ, pred :: a -> a toEnum :: Int -> a fromEnum :: a -> Int enumFrom :: a -> [a] enumFromThen :: a -> a -> [a] enumFromTo :: a -> a -> [a] enumFromThenTo :: a ->Datentypen a -> a -> [a] Algebraische • Selbstdefinierte Typen • Bezug zur Algebra: Ein Typ, Definition durch Angabe von Konstruktoren data Typename = • Aufzählungstypen: Constr1 Typ11 ... | Constr2 Typ21 ... ... | ConstrN TypN1 ... data Season = Spring | Summer | Autumn | Winter data Bool = True | False data Ording = LT | EQ | GT ??? • Funktionen darauf: Wie gehabt, d.h. Pattern-Matching mit Konstruktoren 32 Funktionen über rekursive Datentypen Typklasseninstanzen ableiten • Induktiv definiert => Funktionen darüber: rekursiv • maxDepth maxDepth maxDepth Beispiel: max • Operationen automatisch erzeugen :: IntTree -> Int EmptyIntTree = 0 (Node _ t1 t2) = (maxDepth t1) (maxDepth t2) data Temp = Hot | Cold deriving (Eq, Ord, Enum, Show, Read) 35 Primitive Rekursion für algebraische Datentypen Rekursive Datentypen Gemäß der Definition des Typs: • Für nicht-rekursive Konstuktoren ist der Wert direkt angegeben • Typ ist gleichzeitig Argument für Konstruktor • Für rek. Konstruktoren werden die Werte der Funktion an den rekursiven Teilausdrücken verwendet, um das Ergebnis zu berechnen • Definition ist also induktiv • Beispiel: data IntTree = EmptyIntTree | Node Int IntTree IntTree sumIntTree :: IntTree -> Int sumIntTree EmptyIntTree = 0 sumIntTree (Node n t1 t2) = n + sumIntTree t1 + sumIntTree t2 36 Allgemeine Syntax topdecl -> simpletype constrs constr deriving -> -> -> -> Abstraktion über primtive Rekursion data [context =>] simpletype = constrs [deriving] tycon tyvar1 ... tyvark constr1 | ... | constrn Con atype1 ... atypek deriving (dclass | (dclass1, ... , dclassn)) Allgemein: • Ersetze die Konstruktoren durch Funktionsaufrufe • Für rekrusive Argumente, rufe Abstraktion auf 39 Der Typ Maybe Polymorphe Datentypen • Idee: Einige Argumente der Konstruktoren sind Typvariablen Eingebaut als data Maybe a = Nothing | Just a • Beispiel: data List a = Nil | Cons a (List a) • Entspricht Berechnung, die eventuell fehlgeschlagen ist 40