Algorithmen und Programmieren 1 Funktionale Programmierung - Musterlösung zur Übungsklausur - Punkte: A1: 30, A2: 20, A3: 20, A4: 20, A5: 10, A6: 20 Punkte: /120 12.02.2012 Hinweis: Geben Sie bei allen verwendeten Funktionen die Signaturen an. Viel Erfolg! 1 Haskell-la-vista! (10+10+10=30 Punkte) 1.1 Funktionen höherer Ordnung (10 Teilpunkte) Schreiben Sie unter Verwendung der map und foldr Funktionen eine Funktion sumquad, die bei Eingabe eines ganzzahligen positiven Werts n, die Summe aller Quadratzahlen von 1 bis n berechnet. sumquad :: Integer -> Integer sumquad n (n >= 0) = foldr (+) 0 (map (^2) [1..n]) otherwise = error "wrong input!" 1
1.2 Klassen + Instanzen (10 Teilpunkte) Seien zwei verschiedene algebraische Datentypen, einer für die Darstellung der natürlichen Zahlen (N) und einer für die Darstellung der ganzen Zahlen (Z) gegeben: data N = Zero S (N) deriving (Show) data Z = Z (N,N) deriving (Show) Erklärung: Wenn a die Zahl x darstellt und b die Zahl y darstellt, dann stellt Z (a,b) die Zahl x y dar. Beispiele: Mit dem Datentyp N kann man Zahlen wie folgt darstellen: 3 = S(S(S(Zero))) 7 = S(S(S(S(S(S(S(Zero))))))) Mit dem Datentyp Z kann man Zahlen wie folgt darstellen: 3 = ( S(S(S(Zero))), Zero ) 3 = ( S(S(S(S(Zero)))), S(Zero) ) -2 = ( Zero, S(S(Zero)) ) -2 = ( S(S(Zero)), S(S(S(S(Zero)))) ) Schreiben Sie die Typ-Klasse Rechnen, die Ihnen die Operation Addition bereitstellt und implementieren Sie jeweils eine Instanz für jeden algebraischen Datentypen. class Rechnen a add::a -> a -> a instance Rechnen N add::n -> N -> N add Zero y = y add S(x) y = add x S(y) instance Rechnen Z add::z -> Z -> Z add (Z (a,b)) (Z (x,y)) = Z ((add a x),(add b y)) 2
1.3 Typ-Inferenz (10 Teilpunkte) Es seien folgende Funktionssignaturen bekannt. map :: (a -> b) -> [a] -> [b] sum :: (Num a) => [a] -> a length :: [a] -> Int show :: (Show a) => a -> String Bestimmen Sie den Typ der Funktionen: f = sum. map sum g = map (show. length) f :: (Num a) => [[a]] -> a Erklärung zu f: 01) map hat den Typ (a -> b) -> [a] -> [b] 02) sum hat den Typ Num c => [c] -> c 03) sum hat den Typ Num d => [d] -> d 04) sum hat den Typ a -> b, weil es das erste Argument von map ist 05) a = Num d => [d], wegen 03) und 04) 06) b = Num d => d, wegen 03) und 04) 07) x hat den Typ [a] = 05) Num d => [[d]], weil es das zweite Argument von map ist 08) (map length x) hat den Typ [b] = 06) Num d => [d], wegen 01) 09) (map length x) hat den Typ Num c => [c], weil es das erste Argument von sum ist 10) c = d, wegen 08) und 09) 11) sum (map length x) hat den Typ Num d => d, wegen 02) und 10) Aus 07) und 11) folgt, dass (sum. map length) den Typ Num d => [[d]] -> d hat. g :: [[a]] -> [String] Um den Typ von map (show. length) zu bestimmen, schauen wir zuerst, was der Typ von show. length ist. Nach Denition von (.) gilt: (show. length) x = show (length x) Erklärung zu g: 01) show hat den Typ Show a => a -> String 02) length hat den Typ [b] -> Int 03) x hat den Typ [b], weil es das erste Argument von length ist 04) (length x) hat den Typ Int, wegen 02) 05) (length x) hat den Typ Show a => a, weil es das erste Argument von show ist 06) show (length x) hat den Typ String, wegen 01) 07) (show. length) hat den Typ [b] -> String, wegen 03) und 06) Nun können wir den Typ von map (show. length) bestimmen: 08) map hat den Typ (c -> d) -> [c] -> [d] 09) (show. length) hat den Typ c -> d, weil es das erste Argument von map ist 10) c = [b], wegen 07) und 09) 11) d = String, wegen 07) und 09) Aus 08), 10) und 11) folgt, dass map (show. length) den Typ [[b]] -> [String] hat. 3
2 Datenbank (20 Punkte) Schreiben Sie ein Modul SimpleDB, das einen algebraischen Datentyp Datenbank verwendet um eine einfache Datenbank zu simulieren. Eine Datenbank ist eine Liste von 2-Tupeln. Jedes dieser 2- Tupel hat die Form: (key, [value 1, value 2,..., value n ]), wobei n 1 und jeder Schlüssel einzigartig sein muss (Primärschlüssel). Überlegen Sie sich geeignete Typklassen für die Typen der Schlüssel und der Werte. Es sollen folgende Funktionen implementiert werden: create Erzeugt eine leere Datenbank. Der Rückgabewert ist eine Datenbank. select db key Diese Funktion gibt alle Wert zurück, die einem Schlüssel in einer Datenbank zugeordnet sind. Falls der Schlüssel nicht existiert wird eine leere Liste ausgegeben. Falls der Schlüssel existiert wird eine Liste mit allen Werten, die diesem Schlüssel zugeordnet sind, zurückgegeben. Der Rückgabewert ist eine Liste von Werten. update db key value Diese Funktion fügt einer Datenbank einen Schlüsseleintrag mit zugeordnetem Wert hinzu. Falls der Schlüssel existiert und der Wert noch nicht dem Schlüssel zugeordnet ist, wird der Wert dem Schlüssel zugeordnet. Falls der Schlüssel existiert und der Wert bereits dem Schlüssel zugeordnet ist, wird nichts verändert. Falls der Schlüssel nicht existiert wird dieser erzeugt und der Wert diesem zugeordnet. Der Rückgabewert ist eine Datenbank. drop db key value Diese Funktion löscht aus einer Datenbank einen Wert, der einem Schlüssel zugeordnet war. Falls der Schlüssel oder der Wert nicht existiert wird ein Fehler ausgegeben. Falls der Schlüssel existiert wird der Wert aus der Zuordnung zum Schlüssel entfernt. Falls der Schlüssel existiert und nur noch der angegebene Wert als einziger Wert dem Schlüssel zugeordnet ist, wird der Schlüssel ebenfalls aus der Datenbank entfernt. Der Rückgabewert ist eine Datenbank. Tipp: Hilfreiche Funktionen sind elem, takewhile, dropwhile und filter. 4
module SimpleDB (Datenbank, create, select, update, drop) -- Dieses Modul stellt eine ganz einfache Datenbank dar. data (Eq k, Eq v) => Datenbank k v = DB [(k,[v])] deriving (Show) create :: (Eq k, Eq v) => Datenbank k v select :: (Eq k, Eq v) => Datenbank k v -> k -> [v] update :: (Eq k, Eq v) => Datenbank k v -> k -> [v] -> Datenbank k v drop :: (Eq k, Eq v) => Datenbank k v -> k -> [v] -> Datenbank k v create = DB [] select (DB []) _ = [] select (DB ((key', values'):xs)) key (key' == key) = values' otherwise = select (DB xs) key update (DB xs) key values (iselement (DB xs) key) == False = error "Key existiert nicht!" otherwise = (DB (lokey ++ [(key, values)] ++ rokey)) lokey = (takewhile (\x -> (fst x) /= key) xs) rokey = (tail ((dropwhile (\x -> (fst x) /= key)) xs)) drop (DB xs) key values (iselement (DB xs) key) == False = error "Key existiert nicht!" otherwise = (DB (lokey ++ rokey)) lokey = (takewhile (\x -> (fst x) /= key) xs) rokey = (tail ((dropwhile (\x -> (fst x) /= key)) xs)) -- Diese Funktion kann nach dem Import des Moduls nicht direkt aufgerufen werden. -- Scope = private, da iselement nicht in der Exportliste aufgeführt ist. iselement :: (Eq k, Eq v) => Datenbank k v -> k -> Bool iselement (DB []) _ = False iselement (DB ((key',_):xs)) key = (key' == key) iselement (DB xs) key Eine andere nden Sie auf der Veranstaltungsseite. 5
3 Sortieren und Laufzeit (10+10=20 Punkte) 3.1 Sortieren durch Einfügen (10 Teilpunkte) Implementieren Sie den Insertion-Sort-Algorithmus. isort :: [Integer] -> [Integer] isort [] = [] isort (x:xs) = ins x (isort xs) ins :: Integer -> [Integer] -> [Integer] ins x [] = [x] ins x (y:ys) (x <= y) = (x:y:ys) otherwise = y:(ins x ys) 3.2 Laufzeit (10 Teilpunkte) Bestimmen Sie die Laufzeit Ihrer Implementierung. Es reicht wenn Sie die Laufzeit in O-Notation angeben und diese textuell begründen. Die Funktion isort hat eine Laufzeit von O(n 2 ). isort besteht aus zwei Teilen. Der erste Teil durchläuft alle Elemente der Liste, also n Elemente. Bei jedem Durchlauf wird der zweite Teil aufgerufen. Im zweiten Teil wird jedes Element mit dem Kopf der Restliste verglichen und sortiert eingefügt. Der zweite Teil vergleicht (n 1) Elemente miteinander. Zusammen ergibt das eine Laufzeit von n (n 1) = n 2 n O(n 2 ). 6
4 Strukturelle Induktion (10+10=20 Punkte) 4.1 Bäume (10 Teilpunkte) Sei folgender algebraischer Datentyp und folgende Gleichungen gegeben: data Baum = Blatt Int Knoten Baum Baum deriving (Show) -- wendet die Funktion f auf jedes Element des Baumes an maptree :: (Int -> Int) -> Baum -> Baum maptree f (Blatt x) = Blatt (f x) -- mapt.1 maptree f (Knoten lb rb) = Knoten (maptree f lb) (maptree f rb) -- mapt.2 Beweisen Sie mit struktureller Induktion folgende Behauptung: maptree id baum = id baum Es folgt ein Beweis mit struktureller Induktion über (die Tiefe des Baumes) t. zu zeigen: maptree id baum = id baum Induktionsbasis: t = 0, Baum = Blatt x maptree id Baum[t=0] d.h. maptree id (Blatt x) = id (Blatt x) <==> (Blatt (id x)) = id (Blatt x) -- mapt.1 <==> (Blatt x) = (Blatt x) Induktionsvoraussetzung: Sei die Behauptung wahr für alle DT vom Typ Baum der Tiefe t <= n. Induktionsbehauptung: Die Behauptung ist wahr für alle DT vom Typ Baum der Tiefe t <= n+1. Induktionsschritt: n -> n+1 maptree id Baum[t<=(n+1)] d.h. maptree id (Knoten lb rb) = id (Knoten lb rb) -- mapt.2 <==> (Knoten (maptree id lb) (maptree id rb)) = id (Knoten lb rb) -- nach IV <==> (Knoten (id lb) (id rb)) = id (Knoten lb rb) <==> (Knoten (lb) (rb)) = (Knoten lb rb) Es wurde mit struktureller Induktion gezeigt, dass die Behauptung gilt. 7
4.2 Listen (10 Teilpunkte) Seien folgende Gleichungen gegeben: ++.1 : [] ++ ys = ys ++.2 : (x:xs) ++ ys = x:(xs ++ ys) rev.1 : reverse [] = [] rev.2 : reverse (x:xs) = reverse xs ++ [x] rev.3 : reverse (xs ++ ys) = (reverse ys) ++ (reverse xs) Beweisen Sie mit struktureller Induktion folgende Behauptung: reverse (reverse xs) = xs Es folgt ein Beweis mit struktureller Induktion. Induktionsbasis: xs = [] reverse (reverse []) = [] rev.1 <==> reverse [] = [] rev.1 <==> [] = [] Induktionsvoraussetzung: Für eine Liste xs mit beliebiger, aber fester Länge gilt: reverse (reverse xs) = xs Induktionsbehauptung: Dann gilt auch: reverse (reverse (x:xs)) = (x:xs) Induktionsschritt: xs -> (x:xs) reverse (reverse (x:xs)) = (x:xs) rev.2 <==> reverse (reverse xs ++ [x]) = (x:xs) rev.3 <==> reverse [x] ++ reverse (reverse xs) = (x:xs) <==> reverse (x:[]) ++ reverse (reverse xs) = (x:xs) rev.2 <==> (reverse [] ++ [x]) ++ reverse (reverse xs) = (x:xs) rev.1 <==> ([] ++ [x]) ++ reverse (reverse xs) = (x:xs) ++.1 <==> [x] ++ reverse (reverse xs) = (x:xs) nach IV <==> [x] ++ xs = (x:xs) <==> x:[] ++ xs = (x:xs) ++.2 <==> x:([] ++ xs) = (x:xs) ++.1 <==> (x:xs) = (x:xs) Es wurde mit struktureller Induktion gezeigt, dass die Behauptung gilt. 8
5 Primitive Rekursion (10 Punkte) Rekursionsschema: R(0, x 1,..., x m ) = g(x 1,..., x m ) R(S(n), x 1,..., x m ) = h(r(n, x 1,..., x m ), n, x 1,..., x m ) Zeigen Sie, dass die Funktion isodd (Ein Prädikat, das prüft ob eine Zahl ungerade ist. Das Ergebnis ist 1 wenn die Zahl ungerade ist und 0 sonst.) primitiv-rekursiv ist. Sie dürfen nur die Grundfunktionen S und Z voraussetzen, das heiÿt, alle Funktion die Sie ansonsten für Ihren Beweis verwenden müssen Sie ebenfalls beweisen. not 0 = one not n = Z (not (n-1)) one = S Z isodd 0 = Z isodd n = not (isodd (n-1)) 6 λ - Kalkül (5+5+10=20 Punkte) 6.1 Freie und gebundene Ausdrücke (5 Teilpunkte) Bestimmen Sie die freien und gebundenen Variablen im folgenden Ausdruck und geben Sie diese explizit an. Benennen Sie die gebundenen Variablen so um, dass in jedem Ausdruck alle λ- Abstraktionen verschiedene Variablen binden. (λab.(λab.(λc.(λab.abc))ab(cc))(λab.a)(λac.c(c(a)))) (λab.(λde.(λc.(λfg.fgc))de(xx))(λhi.h)(λjk.k(k(j)))) Gebundene Variablen: a, b, c, d, e, f, g, h, i, j, k Freie Variablen: x 9
6.2 λ - Ausdruck reduzieren (5 Teilpunkte) (λa.a(λab.b)(λa.a(λab.b)(λab.a))(λab.b))(λab.ab) Es gibt 2 Möglichkeiten das Problem zu lösen: Elegante : Normale : Z 1 F {}}{{}}{{}}{ (λa.a (λab.b) (λa.a(λab.b)(λab.a)) (λab.b)) (λab.ab) = (λab.b) }{{}}{{}}{{} F F (λa.a(λab.b)(λa.a(λab.b)(λab.a))(λab.b))(λab.ab) = β (λab.ab)(λab.b)(λa.a(λab.b)(λab.a))(λab.b) = β (λb.(λab.b)b)(λa.a(λab.b)(λab.a))(λab.b) = β (λab.b)(λa.a(λab.b)(λab.a))(λab.b) = β (λb.b)(λab.b) = β (λab.b) F 6.3 λ - Ausdruck erstellen (10 Teilpunkte) Schreiben Sie einen λ - Ausdruck, der rekursiv die Quadrate aller natürlichen Zahlen von 0 bis n addiert. Sie können dabei die folgenden Funktionen als gegeben voraussetzen: A(Addition) M(Multiplikation) P(Vorgänger) Y(Fixpunktoperator) Z(Vergleich auf 0) sumquad :: Integer -> Integer sumquad = if n == 0 then 0 else (n*n) + sumquad (n-1) sumquad Y (λrn. (Zn) (0) (A(M(n)(n))(r(P n)))) }{{}}{{}}{{} if else then 10