Was bisher geschah I Deklarative vs. imperative Programmierung I Deklarative Programmierung: FP, LP, FLP, CLP Funktionale Programmierung in Haskell: I Algebraische Datentypen I Pattern Matching I Polymorphie I Rekursive Datentypen (Peano-Zahlen, Listen, binäre Bäume) I Rekursive Funktionen I strukturelle Induktion 47 Wiederholung λ-Ausdrücke Beispiele: I double :: Int -> Int double x = x + x double = \ x -> x + x I succ :: Int -> Int succ x = x + 1, succ = \ x -> x + 1 I f :: Int -> Int -> Int f x y = 2 * x + y, Anwendung f 5 2 f x = \ y -> 2 * x + y , Anwendung (f 5) 2 f = \ x -> ( \ y -> 2 * x + y ) oder kürzer f = \ x y -> 2 * x + y I g :: a -> b -> a g x _ = x, g x = \ _ -> x, g = \ x y -> x 48 Currying Idee: Jede Funktion mit mehreren Argumenten lässt sich durch Funktionen mit einem Argument darstellen Beispiel: g :: [ a ] -> Int g l n = (len l) < g l = \ n -> (len g = \ l n -> (len -> Bool n l) < n l) < n in mathematischer Notation: (A × B) → C ist isomorph zu A → (B → C) 49 Funktionen höherer Ordnung Funktionen als Argument von Funktionen Beispiel: twice :: (a -> a) -> a -> a twice f x = f (f x) Anwendung: I double hat den Typ Int -> Int I twice double hat den Typ Int -> Int I twice double 3 hat den Typ Int und den Wert ? \x -> 2 * x + 1 hat den Typ Int -> Int I twice (\x -> 2 * x + 1) hat den Typ Int -> Int I I twice (\x -> 2 * x + 1) 3 hat den Typ Int und den Wert ? I succ 0, twice succ 0, twice twice succ 0 I twice (^2) 3, twice twice (^2) 3 50 Beispiele I punktweise Summe zweier Funktionen: fsum :: (a -> Int) -> (a -> Int) -> (a -> Int) fsum f g x = (f x) + (g x) fsum f g = \x -> (f x) + (g x) Beispiele: I I I fsum (*2) (+1) 4, fsum len head [ 2 .. 5 ] Komposition von Funktionen (.) :: (a -> b) -> (b -> c) -> (a -> c) (f . g) x = f (g x) (f . g) = \ x -> f (g x) Beispiel: ( ( \ x -> x * 2 ) . len ) "foo" 51 Wiederholung: rekursive Datentypen I Peano-Zahlen data Nat = Z | S Nat I Listen data List a = Nil {} | Cons { head :: a, tail :: List a} oder kürzer data [a] = [] | a : [a] I Binärbäume data Tree a = Leaf {} | Branch { left :: Tree a, key :: a, right :: Tree a} 52 Wiederholung: Funktionen auf rekursiven Datentypen Entwurf rekursiver Funktionen auf rekursiven Datentypen: 1. Typdefinition 2. Angabe aller Basis- und rekursiven Fälle 3. Definition der Ergebnisse der Basisfälle 4. Definition der Ergebnisse der rekursiven Fälle 5. evtl. Typ verallgemeinern Beispiel: Summe aller Schlüssel eines Baumes data Tree a = Leaf | Branch (Tree a) a (Tree a) 1. Typdefinition: tsum :: Tree Int -> Int 2. Angabe aller Basis- und rekursiven Fälle: tsum t = case t of Leaf -> ... Branch l k r -> ... 3. Definition der Ergebnisse der Basisfälle: Leaf -> 0 4. Definition der Ergebnisse der rekursiven Fälle: Branch l k r -> (tsum l) + k + (tsum r) 53 Wiederholung: Funktionen auf Listen und Bäumen Operationen auf Listen: I Verdoppeln jedes Listenelements I Angabe gerade / ungerade für jedes Listenelement I Länge der Liste I Summe aller Listenelemente Operationen auf Bäumen: I Verdoppeln jedes Schlüssels I Angabe gerade / ungerade für jeden Schlüssel I Anzahl aller Schlüssel I Summe aller Schlüssel I Inorder-Durchquerung 54 Wiederholung: Funktionen auf Listen Beispiel: Verdoppeln jedes Elementes in einer Liste double :: Int -> Int double x = x + x doubles :: [Int] -> [Int] doubles xs = case xs of [] -> [] (y:ys) -> (double y) : (doubles ys) oder mit anonymer Funktion (λ-Notation): doubles :: doubles xs [] -> (y:ys) [Int] -> [Int] = case xs of [] -> ((\ x -> x + x) y) : (doubles ys) evens :: [Int] -> [Bool] evens xs = case xs of [] -> [] (y:ys) -> ((\x->(mod x 2 == 0)) y) : (evens ys) 55 Rekursionsmuster für Listen gemeinsame Eigenschaft: Ergebnis ist die Liste der Funktionswerte jedes Elementes der Eingabeliste I Parameter: I I auf jedes Element anzuwendende Funktion h :: a -> b Liste vom Typ [a] I Ergebnis: Liste vom Typ [b] I Berechnung (Pattern Matching): f xs = case xs of [] -> [] (x : xss) -> ( h x ) : ( f xss ) 56 Rekursionsmuster map Beschreibung des Rekursionsschemas f x = case x of [] -> [] (x : xss) -> ( h x ) : ( f xss ) durch eine Funktion höherer Ordnung mit der Funktion h :: a -> b als Argument map :: ( a -> b ) -> [a] -> [b] Anwendung: f = map h ermöglicht kurze Funktionsdefinition, z.B. doubles :: [ Int ] -> [ Int ] doubles = map double oder mit anonymer Funktion: doubles = map (\z -> z*2) oder noch kürzer: doubles = map ( *2 ) 57 filter Beispiel: nur gerade Zahlen der Eingabeliste ev :: Int -> Bool ev = \x -> ( mod x 2 == 0 ) evens :: [Int] -> [Int] evens xs = case xs of [] -> [] ( x : xss ) -> if ev x then x : ( evens xss ) else ( evens xss ) Funktion höherer Ordnung: filter :: ( a -> Bool ) -> [a] -> [a] filter p xs = case xs of [] -> [] ( x : xss ) -> if ( p x ) then x : ( filter p xss ) else filter p xss 58 filter ev :: Int -> Bool ev = \x -> ( mod x 2 == 0 ) filter :: (a -> Bool) -> [a] -> [a] filter p xs = case xs of [] -> [] ( x : xss ) -> if ( p x ) then x : ( filter p xss ) else filter p xss ermöglicht kurze Funktionsdefinitionen, z.B.: evens = filter ev oder evens = filter ( \x -> ( mod x 2 == 0 ) ) filter ( < 100 ) ( map ( ^2 ) [ 0, 2 .. 10 ] ) 59 List Comprehensions Generatoren: [f x | x <- xs] = map f xs Beispiel: [ x^2 | x <- [1 .. 5]] = map (^2) [1 .. 5] mehrere Generatoren: [ f x1 .. xn |x1 <- .., ... , xn <- .. ] Beispiel: [ x + y | x <- [1 .. 5], y <- [0,10 .. 100]] Man bemerke die Ähnlichkeit zur Mengenschreibweise: {x + y | x ∈ {1, . . . , 5} ∧ x ∈ {0, 10, . . . , 100}} Bedingungen: [f x | x<-xs, p x] = map f (filter p xs) Beispiel: [ x ^ 2 | x <- [1 ..5], mod x 2 == 0] Man bemerke die Ähnlichkeit zur Mengenschreibweise: {x 2 | x ∈ {1, . . . , 5} ∧ x ≡2 0} 60 Beispiele [ [ [ [ [ x ^ 2 | x <- [ 1 ..5 ] ] ev x | x <- [ 1 .. 10 ] ] (x , x ^ 2 ) | x <- [ 1 ..5 ], ev x] (x , y) | x <- [1 ..5], y <- [1 .. 5], x < y] x ^ 2 | x <- [ 0, 1 .. 10 ], x ^ 2 < 100 ] 61 Mehr rekursive Funktionen auf Listen data [a] = [] | a : [a] Länge einer Liste: len :: [a] -> Int len xs = case xs of [] -> 0 ( _ : xss ) -> 1 + (len xss) Summe aller Listenelemente: sum :: [Int] -> Int sum xs = case xs of [] -> 0 ( x : xss ) -> x + (sum xss) 62 Mehr Rekursionsmuster für Listen gemeinsame Eigenschaft: I Parameter: I I I Funktion cons :: a -> b -> b zur Berechnung eines Wertes aus dem bisher berechneten Wert und einem Listenelement Wert nil :: b für leere Eingabeliste Liste vom Typ [a] I Ergebnis vom Typ b I Berechnung (Pattern Matching): f xs = case xs of [] -> nil (x : xss) -> cons x ( f xss ) 63 Rekursionschema fold Funktion höherer Ordnung (mit Funktion als Argument) foldr :: (a -> foldr cons nil [] -> x : xss -> b -> b) -> b -> [a] -> b xs = case xs of nil cons x ( foldr cons nil xss ) ermöglicht kurze Funktionsdefinition, z.B. len = foldr (\ x y -> 1 + x) 0 sum = foldr (\ x y -> x + y) 0 oder kurz: sum = foldr (+) 0 64 Rekursionsschemata über Bäume doubles :: Tree Int -> [Int] doubles t = case t of Leaf -> Leaf Branch l k r -> Branch (doubles l) (k*2) (doubles r) inorder :: Tree a -> [a] inorder t = case t of Leaf -> [] Branch l k r -> ( inorder l ) ++ [ k ] ++ ( inorder r ) sum :: Tree Int -> Int sum t = case t of Leaf -> 0 Branch l k r -> ( sum l ) + k + ( sum r ) 65 Rekursionsschema map über Bäume f :: Tree a -> b f t = case t of Leaf -> Leaf Branch l k r -> Branch (f l) (g k) (f r) Beispiel: f = doubles g = double Rekursionsschema: tmap :: (a -> b ) -> ( Tree a ) -> ( Tree b ) tmap f t = case t of Leaf -> Leaf Branch l k r -> Branch (tmap f l) (f k) (tmap f r) 66 Rekursionsschema fold über Bäume f :: Tree a -> b f t = case t of Leaf -> leaf Branch l k r -> branch (f l) k (f r) Beispiel: f = inorder leaf = [] branch x y z = x ++ [y] ++ z Rekursionsschema: tfold :: (b -> a -> tfold branch leaf t Leaf -> Branch l k r -> b -> b) -> b -> (Tree a) -> b = case t of leaf branch (tfold branch leaf l) k (tfold branch leaf r) 67 Beispiele: fold über Bäume tfold :: (b -> a -> tfold branch leaf t Leaf -> Branch l k r -> b -> b) -> b -> (Tree a) -> b = case t of leaf branch (tfold branch leaf l) k (tfold branch leaf r) inorder = tfold ( \ x y z -> x ++ [y] ++ z ) [] sum = tfold ( \ l k r -> l + k + r ) 0 analog: Anzahl der Blätter, inneren Knoten, Tiefe 68 Zusammenfassung Rekursions-Schemata Datentyp Konstruktor → → Schema Funktion Auswertung eines Rekursionsschemas für ein Objekt eines algebraischen Datentyps: jeden Konstruktor (= Funktionssymbol) durch entsprechende Funktion ersetzen kennen wir schon: Datentyp Rekursionsschema Funktionssymbol f (Konstruktor) = = = Signatur Σ Σ-Algebra (A, J·KA ) Funktion Jf KA auf A 69