Was bisher geschah I Deklarative vs. imperative Programmierung I Deklarative Programmierung: FP, LP, FLP, CLP 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 39 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 40 Currying Idee: Jede Funktion mit mehreren Argumenten lässt sich durch Funktionen mit einem Argument darstellen in mathematischer Notation: (A × B) → C ist isomorph zu A → (B → C) 41 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 (f 3) 2 42 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: fsum (*2) (+1) 4, fsum len head [ 2 .. 5 ] I 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" 43 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} 44 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) 45 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 Summe aller Schlüssel I Inorder-Durchquerung 46 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 λ-Notation: doubles :: doubles xs [] -> (y:ys) [Int] -> [Int] = case xs of [] -> ((\ x -> x + x) y) : (doubles ys) even_test :: [Int] -> [Bool] even_test xs = case xs of [] -> [] (y:ys)->((\x->(mod x 2 == 0))y):(even_test ys) 47 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 : xxs) -> ( h x ) : ( f xxs ) 48 Rekursionsmuster map Beschreibung des Rekursionsschemas f x = case x of [] -> [] (x : xxs) -> ( h x ) : ( f xxs ) 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 = map double oder mit anonymer Funktion doubles = map (\ z -> z + z) even_test = map ( \ x -> ( mod x 2 == 0) ) 49 filter Beispiel: nur gerade Zahlen der Eingabeliste evens :: [Int] -> [Int] evens xs = case xs of [] -> [] (x : xxs) -> if (mod x 2 == 0) then x : (evens xxs) else (evens xxs) Funktion höherer Ordnung: filter :: (a -> Bool) -> [a] -> [a] filter p xs = case xs of [] -> [] (x:xxs) -> if (p x) then x:(filter p xxs) else filter p xxs ermöglicht kurze Funktionsdefinitionen, z.B.: evens = filter ev 50 List Comprehensions Generatoren: [f x | x <- xs] = map f xs Beispiel: [ x ^ 2 | x <- [1 .. 5]] = map (^2) [1 .. mehrere Generatoren: [ f x1 .. xn |x1 <- .., .. xn <- .. ] Beispiel: [ x + y | x <- [1 .. 5], y <- [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] 51 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 ] 52 Mehr rekursive Funktionen auf Listen data [a] = [] | a : [a] Länge einer Liste: len :: [a] -> Int len xs = case xs of [] -> 0 ( _ : xxs ) -> 1 + (len xxs) Summe aller Listenelemente: sum :: [Int] -> Int sum xs = case xs of [] -> 0 ( x : xxs ) -> x + (sum xxs) 53 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 : xs) -> cons x ( f xxs ) 54 Rekursionschema fold Funktion höherer Ordnung (mit Funktion als Argument) fold :: (a -> b -> b) -> b -> [a] -> b fold cons nil xs = case xs of [] -> nil x : xxs -> cons x ( fold cons nil xxs ) ermöglicht kurze Funktionsdefinition, z.B. len = fold (\ x y -> 1 + x) 0 sum = fold (\ x y -> x + y) 0 oder kurz sum = fold (+) 0 55 Rekursionsschemata über Bäume inorder :: Tree a -> [a] inorder t = case t of Leaf {} -> [] Branch {} -> inorder (left t) ++ [key t] ++ inorder (right t) sum :: Tree Int -> Int sum t = case t of Leaf {} -> 0 Branch {} -> sum (left t) + key t + sum (right t) 56 Rekursionsschema fold über Bäume f :: Tree a -> b f t = case t of Leaf {} -> leaf Branch {} -> branch (f (left t)) (key t) (f (right t)) f = inorder leaf = [] ; branch x y z = x ++ [y] ++ z tfold :: (b -> a -> b -> b) -> b -> (Tree a) -> b tfold branch leaf t = case t of Leaf {} -> leaf Branch {} -> branch (tfold branch leaf $ left t) (key t) (tfold branch leaf $ right t) inorder = fold ( \ x y z -> x ++ [y] ++ z ) [] sum = fold ( \ l k r -> l + k + r ) 0 57 Zusammenfassung /Rekursions-Schemata Datentyp → Schema Konstruktor → Funktion Auswertung eines Rekursionsschemas für ein Objekt eines algebraischen Datentyps: jeden Konstruktor (= Funktionssymbol) durch die entsprechende Funktion ersetzen Datentyp = Signatur Σ, Rekursionsschema = Σ-Algebra. 58