Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 5 Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 (6+9 = 15 Punkte) a) Bestimmen Sie jeweils den Typ der folgenden Haskell-Ausdrücke: (’1’, ’2’:"3", 4 < 5) :: (Char, String, Bool) [(last, tail), (head, take 5)] :: [ ([a] -> a, [b] -> [b]) ] [map (const True), map not] [ [Bool] -> [Bool] ] :: b) Gegeben seien die folgenden Funktionsdefinitionen. Bestimmen Sie jeweils den allgemeinsten Typ der Funktionen. Denken Sie auch an eventuelle Klasseneinschränkungen. Hinweis: Auftretende Zahlenkonstanten können Sie als Int interpretieren. f f x y z elemIndex elemIndex x xs g :: = :: head (drop x y) <= z Eq a => a -> [a] -> Maybe Int = case [i | (y,i) <- zip xs [0..], y == x] of [] -> Nothing (i:_) -> Just i :: (Ord a, Num a) => [a] -> a -> [a] -> [a] g x y z = either :: either f _ (Left x) either _ g (Right y) Ord a => Int -> [a] -> a -> Bool = = if sum x < y then x ++ z else tail x (a -> c) -> (b -> c) -> Either a b -> c f x g y 5 Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 6 Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 (9+11 = 20 Punkte) a) Definieren Sie eine Funktion zipWith :: (a -> b -> c) -> [a] -> [b] -> [c], welche als eine Verallgemeinerung der Funktion zip zwei Listen mittels einer übergebenen Funktion zu einer zusammenfügt. Die Ergebnisliste soll wie bei zip die Länge der kürzeren Argumentliste besitzen. Beispiel: zipWith (+) [1,2,3] [4,5,6,7,8] = [5,7,9] zip = zipWith (,) Geben Sie für diese Funktion drei verschiedene Implementierungen an: i) mittels expliziter Rekursion und ii) durch eine Listenkomprehension und Verwendung von zip und iii) durch eine geeignete Anwendung von map und zip Hinweis: Für diese Aufgabe können die Definitionen des Haskell Prelude-Moduls benutzt werden. Wenn Sie darüber hinaus Hilfsfunktionen verwenden, so müssen Sie diese auch definieren. i) zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys zipWith _ _ _ = [] -- auch zipWith zipWith zipWith korrekt: f [] _ = [] f _ [] = [] f (x:xs) (y:ys) = f x y : zipWith f xs ys ii) zipWith f xs ys = [f x y | (x,y) <- zip xs ys] iii) zipWith f xs ys = map (\(x,y) -> f x y) (zip xs ys) 6 Dr. A. Raschke Prof. Dr. H. Partsch Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 b) Die Prüfziffer bei einer ISBN-10 Nummer berechnet sich, indem die vorangehenden 9 Ziffern, gewichtet mit ihrer jeweiligen Position (beginnend bei 1), aufaddiert werden und der Rest der ganzzahligen Division durch 11 genommen wird (ein Rest von 10 wird üblicherweise als Ziffer ’X’ geschrieben, dies muss hier aber nicht beachtet werden): ! 9 X z10 = mod 11 i · zi i=1 Definieren Sie eine Funktion isbn10 :: [Int] -> Int, welche eine Liste von neun Ziffern bekommt und daraus die ISBN-10 Prüfziffer berechnet. Geben Sie für diese Funktion zwei unterschiedliche Implementierungen an: i) Unter Verwendung einer rekursiven Hilfsfunktion über die Liste mit einem inkrementellen (also hochzählendem) Parameter und ii) mittels geschickter Funktionskomposition und der in a) definierten Funktion zipWith. Hinweis: Für diese Aufgabe können die Definitionen des Haskell Prelude-Moduls benutzt werden. Wenn Sie darüber hinaus Hilfsfunktionen verwenden, so müssen Sie diese auch definieren. i) isbn10 :: [Int] -> Int isbn10 xs = makeSum 1 xs ‘mod‘ 11 where makeSum _ [] = 0 makeSum i (x:xs) = i*x + makeSum (i+1) xs ii) isbn10 = (‘mod‘ 11) . sum . zipWith (*) [1..] 7 -- [1,2,3,4,5,6,7,8,9] Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 7 Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 (6+10+3 = 19 Punkte) Gegeben sei folgender Datentyp zur Repräsentation von booleschen Ausdrücken: data BoolExpr = Value Bool | And BoolExpr BoolExpr | Not BoolExpr Der Ausdruck (¬False ∧ True) ∧ True wird z.B. dargestellt als: And (And (Not (Value False)) (Value True)) (Value True) a) Schreiben Sie eine rekursive Funktion eval :: BoolExpr -> Bool, die einen booleschen Ausdruck auswertet. eval eval eval eval :: BoolExpr (Value b) (And e1 e2) (Not e) -> Bool = b = (eval e1) && (eval e2) = not (eval e) b) Definieren Sie eine Funktion foldBoolExpr zur Faltung boolescher Ausdrücke gemäß der in der Vorlesung dargestellten Regeln zur Faltung von allgemeinen Datentypen. Geben Sie auch den Typ Ihrer Funktion an. foldBoolExpr :: -> -> -> -> (Bool -> a) (a -> a -> a) (a -> a) BoolExpr a foldBoolExpr val_f and_f not_f expr = f expr where f (Value b) = val_f b f (And e1 e2) = and_f (f e1) (f e2) f (Not e) = not_f (f e) c) Definieren Sie eval als Instanz von foldBoolExpr. eval = foldBoolExpr id (&&) not 8 Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 8 Paradigmen der Programmierung SS 11 Prüfungsklausur 25.07.2011 (6 Punkte) Ein Gray-Code ist eine Sequenz von binären Codewörtern mit der Eigenschaft, dass sich benachbarte Codewörter lediglich um ein einziges Bit unterscheiden. Definieren Sie eine Funktion gray :: Int -> [String], die für n ≥ 1 die n-Bit Gray-Code Sequenz zurückliefert. Beispiel: gray 1 = ["0","1"] gray 2 = ["00","01","11","10"] gray 3 = ["000","001","011","010","110","111","101","100"] Überlegen Sie sich hierzu, wie der jeweilige n-Bit Code aus dem (n − 1)-Bit Code erzeugt werden kann. Hinweis: Für diese Aufgabe können die Definitionen des Haskell Prelude-Moduls benutzt werden. Wenn Sie darüber hinaus Hilfsfunktionen verwenden, so müssen Sie diese auch definieren. gray :: Int -> [String] gray 0 = [""] gray n = let xs = gray (n-1) in map (’0’:) xs ++ map (’1’:) (reverse xs) oder mit Listenkomprehension: gray :: Int -> [String] gray 0 = [""] gray n = [ ’0’ : x | x <- prev ] ++ [ ’1’ : x | x <- reverse prev ] where prev = gray (n-1) Auch korrekt: ohne 0-Bit Codewort gray 1 = ["0", "1"] gray n = ... 9 Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 1 Paradigmen der Programmierung SS 11 Prüfungsklausur 26.09.2011 (6+9 = 15 Punkte) Haskell: Typisierung a) Bestimmen Sie jeweils den Typ der folgenden Haskell-Ausdrücke: map putChar [’a’..’z’] :: [IO ()] (const ’1’, map (const "2")) :: (a -> Char, [b] -> [String]) [Left "Error", Right True] [Either String Bool] :: b) Gegeben seien die folgenden Funktionsdefinitionen. Bestimmen Sie jeweils den allgemeinsten Typ der Funktionen. Denken Sie auch an eventuelle Klasseneinschränkungen. f f x y g g w x y z :: = :: = nub :: nub = err :: err (Left msg) = err (Right val) = [[a]] -> [[a]] -> [a] concat (x ++ y) (Ord a, Num b) => a -> a -> [b] -> [b] -> b if w < x then product y else sum z Eq a => [a] -> [a] foldr (\x xs -> if x ‘elem‘ xs then xs else x:xs) [] Show a => (Either a b) -> IO (Maybe b) do putStrLn (show msg) return Nothing do putStrLn "Success!" return (Just val) 0 Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 2 Paradigmen der Programmierung SS 11 Prüfungsklausur 26.09.2011 (5+6+4 = 15 Punkte) Haskell: Listen und IO Für die folgenden Aufgaben dürfen Sie alle auf dem Übersichtsblatt angegebenen vordefinierten Funktionen verwenden. Alle anderen verwendeten Funktionen müssen explizit definiert werden. a) Schreiben Sie eine Funktion getNth :: [a] -> Int -> Maybe a, welche als ersten Parameter eine Liste l und als zweiten Parameter eine ganze Zahl n erhält. Diese Funktion liefert das n-te Element (bei 0 angefangen zu zählen) der übergebenen Liste. Kann dieser Wert nicht ermittelt werden (übergebener Index zu groß oder zu klein), soll Nothing zurückgeliefert werden. Beispiel: Prelude> getNth "Alexander" 5 Just ’n’ getNth getNth getNth getNth [] _ = _ n | (x:xs) (x:xs) Nothing n < 0 = Nothing 0 = Just x n = getNth xs (n-1) b) Schreiben Sie eine Funktion getandremoveNth :: [a] -> Int -> (Maybe a,[a]) welche ähnlich zu der Funktion getNth funktioniert, aber zusätzlich eine neue Liste zurückliefert, in der das n-te Element (bei 0 angefangen zu zählen) entfernt wurde. Sie dürfen dazu auch die Funktion getNth benutzen. Bei einem fehlerhaften Index (zu groß oder zu klein) soll Nothing und die ursprüngliche Liste zurückgeliefert werden. Beispiel: Prelude> getandremoveNth "Alexander" 5 (Just ’n’,"Alexader") getandremoveNth l n = let elem = getNth l n in case elem of Nothing -> (Nothing, l) Just x -> (elem, (take n l) ++ (drop (n+1) l)) 1 Dr. A. Raschke Prof. Dr. H. Partsch Aufgabe 3 Paradigmen der Programmierung SS 11 Prüfungsklausur 26.09.2011 (9+12+6+3 = 30 Punkte) Haskell: Bäume und Faltung Eine Schlauchleitung bei der Feuerwehr besteht (stark vereinfacht) aus Schläuchen, Verteilern, und Strahlrohren. Die Schläuche und Strahlrohre haben zwei verschiedene Größen, welche mit B und C bezeichnet werden. Ein Verteiler hat einen B-Eingang, links und rechts C-Abgänge und in der Mitte einen B-Abgang. Von den Schlauchgrößen abgesehen, können Schläuche und Verteiler beliebig kombiniert werden. Ein Strahlrohr kann geöffnet (true) oder geschlossen (false) sein. An einem Verteiler sind immer alle Abgänge offen. Diese Zusammenhänge können zum Beispiel mit folgenden Datentypen beschrieben werden: data Groesse = B | C deriving Eq data Leitung = Rohr Groesse Bool | Schlauch Groesse Leitung | Verteiler Leitung Leitung Leitung Die Konfiguration der im Bild skizzierten Schlauchleitung wird dann durch folgenden Ausdruck dargestellt: bsp = Schlauch B (Verteiler (Schlauch C (Schlauch C (Rohr C True))) (Schlauch B (Rohr B True)) (Rohr C False)) Mit diesen Datentypen ist es möglich, falsche Schlauch-/Anschlussgrößen zusammenzusetzen. Zum Beispiel macht Schlauch B (Schlauch C (Schlauch B)) in der Realität keinen Sinn. Es kann aber für alle folgenden Aufgaben davon ausgegangen werden, dass die jeweils übergebene Schlauchleitung korrekt zusammengesetzt ist. 3 Dr. A. Raschke Prof. Dr. H. Partsch Paradigmen der Programmierung SS 11 Prüfungsklausur 26.09.2011 a) Schreiben Sie eine rekursive Funktion gesamtlaenge :: Leitung -> Int, die die Gesamtlänge der eingesetzten Schläuche bestimmt. C-Schläuche haben eine normierte Länge von 15m, B-Schläuche eine Länge von 20m. Die Länge von Strahlrohren und Verteilern wird bei der Längenbestimmung nicht berücksichtigt. Beispiel: Prelude> gesamtlaenge bsp 70 gesamtlaenge (Schlauch x l) | x == B = gesamtlaenge | x == C = gesamtlaenge gesamtlaenge (Verteiler l1 l2 l3) = gesamtlaenge gesamtlaenge gesamtlaenge gesamtlaenge _ = 0 l + 20 l + 15 l1 + l2 + l3 b) Definieren Sie eine Funktion foldLeitung zur Faltung des obigen Datentyps gemäß der in der Vorlesung dargestellten Regeln zur Faltung von allgemeinen Datentypen. Geben Sie auch den Typ Ihrer Funktion an. foldLeitung :: (Groesse -> Bool -> a) -> (Groesse -> a -> a) -> (a -> a -> a -> a) -> Leitung -> a foldLeitung rf sf vf l = f l where f (Rohr g b) = rf g b f (Schlauch g l) = sf g (f l) f (Verteiler l1 l2 l3) = vf (f l1) (f l2) (f l3) 4 Dr. A. Raschke Prof. Dr. H. Partsch Paradigmen der Programmierung SS 11 Prüfungsklausur 26.09.2011 c) Definieren Sie eine Funktion wassermenge :: Leitung -> Int als Instanz von foldLeitung, welche die aktuell abgegebene Wassermenge einer Schlauchleitung bei einem festgelegten Druck angibt. Folgende Faustwerte werden dafür eingesetzt: C-Strahlrohre geben 100 l/min Wasser ab, B-Strahlrohre 400 l/min. Verteiler und Schläuche werden nicht berücksichtigt (obwohl sie in der Realität natürlich einen Einfluss haben). Bei der Berechnung soll allerdings schon berücksichtigt werden, ob das entsprechende Strahlrohr geöffnet oder geschlossen ist! Beispiel: Prelude> wassermenge bsp 500 wassermenge :: Leitung -> Int wassermenge = foldLeitung (\g b -> if b then (if g==C then 100 else 400) else 0) (\g l -> l) (\l1 l2 l3 -> l1 + l2 + l3) d) Wie müssten die Datentypdefinitionen verändert werden, damit nur noch korrekte Schlauchleitungen instanziiert werden können? Es soll also nicht mehr möglich sein, dass zum Beispiel nach einem B-Schlauch eine Leitungskomponente (Schlauch oder Rohr) mit C-Größe angegeben werden kann. Geben Sie Vorschläge für die Datentypdefinitionen an! data BLeitung = | | data CLeitung = | BRohr Bool BSchlauch BLeitung Verteiler CLeitung BLeitung CLeitung CRohr Bool CSchlauch CLeitung 5