ProInformatik, Musterlösung Tag 18 und 19 Funktionen höherer Ordnung Aufgabe 1 length :: [a] -> Int length xs = (sum . map (\x -> 1)) xs Aufgabe 2 allGreaterZero :: (Num a, Ord a) => [a] -> Bool allGreaterZero xs = (((==) 0) .length . filter (\x -> (x < 1))) xs Aufgabe 3 iter :: Int -> (a -> a) -> a -> a iter 0 _ a = a iter n f a = f (iter (n - 1) f a) Aufgabe 4 sumOfSquares :: (Num a) => [a] -> a sumOfSquares xs = (foldr (+) 0 . map (^ 2)) xs Typherleitung, Typüberprüfung Aufgabe 5 Nein, auf Grund des Kommas in der Liste ("[x, Double]"). Aufgabe 6 (i) - (ii) instance (Ord a, Ord b) => Ord (a,b) where (<=) (a,b) (c,d) = (a < c) || (a == c) && (b <= d) instance Ord (<=) (<=) (<=) b => Ord [b] where [] _ = True _ [] = False (a:as) (b:bs) = (a < b) || (a == b) && as <= bs (iii) a) b) c) d) e) Typkorrekt, Wert: True. Typkorrekt, Wert: False. Nicht typkorrekt, da [[Int]] mit einer [Int] verglichen wird. Typkorrekt, Wert: True Typkorrekt, Wert: True, gemeint war (1,2)<(2,1) Aufgabe 7 Nicht vollständig, da Herleitungen fehlen a) map :: (a -> b) -> [a] -> [b] zip :: [a] -> [b] -> [(a, b)] [[a]] -> [[b] -> [(a, b)]] b) uncurry :: (a -> b -> c) -> (a, b) -> c id :: a -> a ((b -> b1 -> c, b), b1) -> c c) uncurry :: (a -> b -> c) -> (a, b) -> c zip :: [a] -> [b] -> [(a, b)] ([a], [b]) -> [(a, b)] d) curry :: ((a, b) -> c) -> a -> b -> c Nicht korrekt, weil die Signatur von curry nicht zu ((a, b) -> c) (curry) passt Aufgabe 8 class Visible t where toString :: t -> String size :: t -> Int instance (Num a) => Visible [a] -- "instance Visible [Int]" geht nicht where toString xs = show xs size xs = length xs Textaufgabe Aufgabe 9 type Name = String; type Note = Float; type Arbeit = (Name, Note); type Ergebnisse = [Arbeit] besteSchüler :: Ergebnisse -> [Name] besteSchüler erg = [ name | (name,note) <- erg, note == 1.0 ] eintrag :: Ergebnisse -> Arbeit -> Ergebnisse eintrag erg a = a:erg zeugnisNote :: Ergebnisse -> Name -> Note zeugnisNote erg name = mw [ no | (na,no) <- erg, name == na ] mw :: [Float] -> Float mw [] = error "Der Mittelwert kann nicht von einer leeren Liste berechnet werden." mw l = sum l / fromIntegral (length l) mittelwert :: Ergebnisse -> Note mittelwert erg = mw [ note | (name,note) <- erg ] Aufgabe 10 Die Aufgabe ist nicht ganz vollständig, zu einer Ausleihe gehören auch Angaben über die Personen, die ausleihen. type type type type Titel = String Anzahl = Int Artikel = (Titel, Anzahl) Bestand = [Artikel] type Ausleiher = String type Ausleih = (Ausleiher, Titel) type Ausleihe = [Ausleih] lJustify :: Int -> Char -> String -> String lJustify n c s | Prelude.length s < n = lJustify n c (s ++ [c]) | otherwise = s showArtikel :: Artikel -> String showArtikel (t, a) = lJustify 25 ' ' t ++ show a showAllArtikel :: Bestand -> String showAllArtikel [] = "" showAllArtikel (x:xs) = showArtikel x ++ "\n" ++ showAllArtikel xs showBestand :: Bestand -> IO () showBestand = putStr . ("Bestand:\n" ++) . showAllArtikel showAusleih :: Ausleih -> String showAusleih (n, t) = lJustify 25 ' ' n ++ t showAllAusleihe :: Ausleihe -> String showAllAusleihe [] = "" showAllAusleihe (x:xs) = showAusleih x ++ "\n" ++ showAllAusleihe xs showAusleihe :: Ausleihe -> IO () showAusleihe = putStr . ("Ausleihe:\n" ++) . showAllAusleihe ZF-Notation Aufgabe 11 cantor :: Int -> [(Int,Int)] cantor 0 = [(0,0)] cantor k | not (even k) = cantor (k-1) ++ zip (reverse([i|i<-[0..k]])) [i|i<-[0..k]] | otherwise = cantor (k-1) ++ zip [i|i<-[0..k]] (reverse([i|i<-[0..k]])) cantor gibt die Liste aller Diagonalen von 0 . . . k. Um die Aufgabe zu lösen muss man von dieser Liste nur noch die ersten k Elemente nehmen. Aufgabe 12 a) Eine rekursive Definition für tud (teste und verdopple) tud :: Num t => (t->Bool) -> [t] -> [t] tud p [] = [] tud p (a:l) | p a = (2 * a) : tud p l | otherwise = tud p l {- eine Beispielrechnung: Main> tud istGerade [1..10] [4,8,12,16,20] :: [Int] b) Eine nichtrekursive Definition für tud unter Verwendung von map und filter tud' :: Num t => (t->Bool) -> [t] -> [t] tud' p l = map (2*) (filter p l) c) Eine Definition für tud unter Verwendung der ZF-Listennotation tud'' :: Num t => (t->Bool) -> [t] -> [t] tud'' p l = [2 * a | a <- l, p a ] Aufgabe 13 zensur :: String -> String -> String zensur w s = concatWith (check (words s) w) " " where concatWith :: [[a]] -> [a] -> [a] concatWith [] _ = [] concatWith [e] _ = e concatWith (e1:e2:xs) w = concatWith ((e1 ++ w ++ e2):xs) w check :: [String] -> String -> [String] check [] _ = [] check (x:xs) w | w == x = (replicate (length x) 'x'): check xs w | otherwise = x:check xs w Lambda-Kalkül Aufgabe 14 a) (λp. (λq. (p) q) (λx.x) λa. λb. a) λk.k Ist korrekt, Freie Variable: keine Reduktion: (λp. (λq. (p) q) (λx.x) λa. λb. a) λk.k -> (λq. (λk.k) q) (λx.x) λa. λb. a -> (λk.k) (λx.x) λa. λb. a -> (λx.x) λa. λb. A -> λa. λb. a b) (λx. (λy. (λz. (x) λx. (x) y))) y x z Nicht korrekt, weil "(...) x y z" und es gilt <Application> := (<λ-Ausdruck>) <λ-Ausdruck> c) (((λf. λg. λx. ((f) g) x) λs.(s)s) λa. λb.b) λx. λy.x Ist korrekt, Freie Variable: keine Reduktion: (((λf. λg. λx. ((f) g) x) λs.(s)s) λa. λb.b) λx. λy.x -> (( λg. λx. ((λs.(s)s) g) x) λa. λb.b) λx. λy.x -> ( λx. ((λs.(s)s) λa. λb.b) x) λx. λy.x -> ((λs.(s)s) λa. λb.b) λx. λy.x -> ((λa. λb.b)λa. λb.b) λx. λy.x -> (λb.b) λx. λy.x -> λx. λy.x Vollständige Induktion Aufgabe 15 Sei M eine Menge von natürlichen Zahlen mit |M|=n Є N. Zeigen Sie, dass für alle natürlichen Zahlen n gilt: |P(M)|=2n, wobei P(M) die Potenzmenge von M ist. Beweis durch vollst. Induktion über n. Induktionsanfang: Sei n=0, dann ist P(M) = {{}} -> |P(M)| = 1 2 ^ n= 2^0=1 Induktionsschritt: Induktionsvoraussetzung: Sei |M|=n mit n, bel. und fest aus N. Dann gilt: |P(M)| = 2^n Induktionsbehauptung: Für |M|=n+1 ist |P(M)| = 2^(n+1) Beweis: Sei |M|=n. Wird ein weiteres Element x in M eingefügt mit |M U {x}|=n+1 dann gilt für die Potenzmenge: | P(M U {x}) |= |P (M) U {x U a|a Є P(M)}| =|P (M)| +| {x U A|AЄP(M)}| = 2^n + 2^n=2^(n+1) Aufgabe 16 Zeige, dass für alle Listen xs der Länge n, f.a. n Є N gilt: f p xs = map (2*) (filter p xs) Induktionsanfang: f p [] = [] (f.1) map (2*) (filter p []) = [] (filter.1, map.1) Induktionsschritt: Sei xs Liste mit bel. fester Länge n. Induktionsvoraussetzung: (I.V.) es gilt f p xs = map (2*) (filter p xs) Induktionsbehauptung: f p (x:xs) = map (2*) (filter p (x:xs)) 1.Fall (p x ergibt True) f p (x:xs) = (2 * x) : f p xs (f.2) = (2 * x) : map (2*) (filter p xs) (I.V.) map (2*) (filter p (x:xs)) = map (2 *) (x : filter p xs) (filter.2) = (2 * x) : map (2 *) (filter p xs) (map.2) 2.Fall (p x ergibt False) f p (x:xs) = f p xs (f.2) = map (2*) (filter p xs) (I.V.) map (2 *) (filter p (x:xs)) = map (2 *) (filter p xs) (filter.2) Aufgabe 17 Zeige, dass für alle Bäume b vom Typ Baum a gilt: occurs b a = length (filter (==a) (collapse b)) Induktionsanfang: doppelt f [] = [] doppelt' f [] = map (f.f) [] = [] (doppelt.1) (doppelt') (map.1) Induktionsschritt: Zeige doppelt f (a:l) = doppelt' f (a:l) unter der Induktionsannahme (I.A.), dass doppelt f l = doppelt' f l gilt. doppelt f (a:l) = (f(f a)) : doppelt f l = (f.f) a : doppelt f l = (f.f) a : doppelt' f l doppelt' f (a:l) = map (f.f) (a:l) = (f.f) a : map (f.f) l = (f.f) a : doppelt' f l (doppelt.2) (.) (I.A.) (doppelt') (map.2) (doppelt' rückwärts) Laufzeitanalyse Aufgabe 19 In der Klausur müssen Sie bitte begründen, warum Sie worauf kommen. Hier nur in Kürze die Rechnung: -- a) Tins'(0) = 1 1. Guard: Tins'(n) = 1 + (n - 1) + (n - 1) + n + n + 1 -- für reverse, und anschließende (++) -> O(n) -- da nach einem rekursiven Aufruf erstes Pattern greift. 2. Guard: Tins'(n) = Tins'(n - 1) + 1 -> O(n) Tins(0) = 1 Tins(n) = n + Oins'(n) -> O(n) -- b) Hier müssen jetzt mehrere Fälle betrachtet werden: 1. Fall, die Knoten sind gleichmäßig verteilt, der Baum ist balanciert: Tcol(0) = 1 Tcol (n) = Tcol((n - 1) / 2) +(( n - 1) / 2) + 1 = 2* Tcol((n - 1) / 2) +(( n - 1) / 2) -- es wird die Summe gebildet von n/2+n/4+n/8+...+1=c*n -> O(n) 2. Fall, Baum ist entartet zur linken Seite: Tcol(0) = 1 Tcol (n) = Tcol(n - 1) +( n - 1) +1 = Summe von 1 bis n -> O(n^2) 3. Fall, Baum ist entartet zur rechten Seite: Tcol(0) = 1 Tcol (n) = Tcol(n - 1) +1 +1 = c*n -> O(n) -- c) Gleicher Analyseweg wie in b) -- d) tiefe :: (Eq a) => a -> Baum a -> Int tiefe s b = find s b 0 where find :: (Eq a) => a -> Baum a -> Int -> Int find _ Leer _ = -1 find s (B l e r) a | s == e = a | otherwise = max (find s l (a + 1)) (find s r (a + 1)) 1. Fall: Element s ist die Wurzel 1. Guard -> O(1) 2. Fall: Worst case: Element bestimmt die Höhe des Baumes und alle Zweige müssen überprüft werden. Der Einfachheit halber wird angenommen, dass der Baum im Durchschnitt ausgeglichen ist, dass also jeder Teilbaum die gleiche Anzahl an Knoten enthält: Ttie(0) = 1 Ttie (n) = Ttie ((n - 1) / 2) + Ttie ((n - 1) / 2) + 1 -> O(n) 3. Fall: Element befindet sich im Durchschnitt zwischen Wurzel und dem höchsten Blatt, dann beträgt die Laufzeit im Schnitt die Hälfte von Fall 2, also O(n/2) -> O(n). Algebraische Datentypen Aufgabe 20 a) data MischFarbe = F Farbe | MF MischFarbe MischFarbe b) colour colour colour colour :: Farbe -> String Rot = "R" Gelb ="G" Blau ="B" instance Show MischFarbe where show (F f) = colour f show (MF mf f) = show mf ++ colour f Aufgabe 21 a) data Baum a = Leer | B (Baum a) a Int (Baum a) deriving (Eq, Ord, Show) -- b) baum :: Baum Int baum = (B (B (B (B (B Leer 1 4 Leer) 2 3 Leer) 3 2 Leer) 4 1 Leer) 5 0 Leer) -- c) inorder :: Baum a -> [(a, Int)] inorder Leer = [] inorder (B l x t r) = inorder l ++ [(x, t)] ++ inorder r -- d) insert :: (Ord a) => Baum a -> a -> Baum a insert Leer e = (B Leer e 0 Leer) insert (B l x t r) e | l == Leer && e <= x = (B (B Leer e (t + 1) Leer) x t r) | e <= x = (B (insert l e) x t r) | r == Leer && e > x = (B l x t (B Leer e (t + 1) Leer)) | e > x = (B l x t (insert r e)) Sortieralgorithmen Aufgabe 22 quickSort :: (Ord a) => [a] -> [a] quickSort [] = [] quickSort (x:xs) = quickSort [y | y <- xs, y > x] ++ [x] ++ quickSort [z | z <- xs, z < x] insertionSort :: (Ord a) => [a] -> [a] insertionSort [] = [] insertionSort (x:xs) = insert (insertionSort xs) x where insert :: (Ord a) => [a] -> a -> [a] insert [] e = [e] insert (x:xs) e | e == x = x:xs | e > x = e:x:xs | otherwise = x:insert xs e Aufgabe 23 ordTrip :: (Ord a) => [a] -> [a] ordTrip [] = [] ordTrip _ = [] ordTrip (x1:x2:x3:xs) = quickSort [x1, x2, x3] ++ ordTrip xs where quickSort :: (Ord a) => [a] -> [a] quickSort [] = [] quickSort (x:xs) = quickSort [y | y <- xs, y <= x] ++ [x] ++ quickSort [z | z <- xs, z > x] Aufgabe 24 sortB :: (Ord a) => [a] -> [a] sortB [] = [] sortB (x:xs) = inorder (sort (insert Leer x) xs) where sort :: (Ord a) => Baum a -> [a] -> Baum a sort b [] = b sort b (x:xs) = sort (insert b x) xs inorder :: Baum a -> [a] inorder Leer = [] inorder (B l x t r) = inorder l ++ [x] ++ inorder r Aufgabe 25 Implementieren Sie den ADT Stack (Stapel) in Haskell mit Hilfe eines Moduls. Der Stack s ist definiert durch die Funktionen: push(e) -- fügt ein Element e in den Stack pop() -- gibt das zuletzt in s eingefügte Element zurück und löscht es top() -- gibt das zuletzt in s eingefügte Element zurück isEmpty() -- gibt an, ob der Stack leer ist Ihre Implementierung kann eine Liste benutzen sollte aber effizient sein, d.h. alle Funktionen sollten Laufzeit O(1) haben. module Stack (Stack, push, pop, top, isEmpty) where data Stack a = L | S a (Stack s) push x Leer = S x Leer push x stack = S x stack pop Leer = error “Stack ist leer” pop (S x rest) = (x,rest) top Leer = error “Stack ist leer” top (S x rest) = x isEmpty Leer = True isEmpty l = False