Algorithmen und Programmieren 1

Werbung
Algorithmen und Programmieren 1
Funktionale Programmierung
- Musterlösung zur Übungsklausur Punkte:
A1:
30,
A2:
20,
A3:
Punkte:
20,
A4:
20,
A5:
10,
A6:
20
/120
12.02.2012
Hinweis: Geben Sie bei allen verwendeten Funktionen die Signaturen an.
1
Viel Erfolg!
(10+10+10=30 Punkte)
Haskell-la-vista!
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.
Lösung
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.
Lösung
class Rechnen a
where
add::a -> a -> a
instance Rechnen N
where
add::N -> N -> N
add Zero y = y
add S(x) y = add x S(y)
instance Rechnen Z
where
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
sum
length
show
::
::
::
::
(a -> b) -> [a] -> [b]
(Num a) => [a] -> a
[a] -> Int
(Show a) => a -> String
Bestimmen Sie den Typ der Funktionen:
f = sum . map sum
g = map (show . length)
Lösung
f :: (Num a) => [[a]] -> a
Erklärung zu f:
map hat den
sum hat den
sum hat den
sum hat den
01)
02)
03)
04)
05)
06)
07)
08)
09)
10)
11)
Typ (a -> b) -> [a] -> [b]
Typ Num c => [c] -> c
Typ Num d => [d] -> d
Typ a -> b, weil es das erste Argument von map ist
a = Num d => [d], wegen 03) und 04)
b = Num d => d, wegen 03) und 04)
x hat den Typ [a] =05) Num d => [[d]], weil es das zweite Argument von map ist
(map length x) hat den Typ [b] =06) Num d => [d], wegen 01)
(map length x) hat den Typ Num c => [c], weil es das erste Argument von sum ist
c = d, wegen 08) und 09)
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
(20 Punkte)
Datenbank
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 2Tupel hat die Form: (key, [value1 , value2 , ... , valuen ]), 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
Lösung
module SimpleDB (Datenbank, create, select, update, drop) where
-- Dieses Modul stellt eine ganz einfache Datenbank dar.
data (Eq k, Eq v) => Datenbank k v = DB [(k,[v])] deriving (Show)
create
select
update
drop
::
::
::
::
(Eq
(Eq
(Eq
(Eq
k,
k,
k,
k,
Eq
Eq
Eq
Eq
v)
v)
v)
v)
=>
=>
=>
=>
Datenbank
Datenbank
Datenbank
Datenbank
k
k
k
k
v
v -> k -> [v]
v -> k -> [v] -> Datenbank k v
v -> k -> [v] -> Datenbank k v
create = DB []
select
select
|
|
(DB []) _ = []
(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))
where
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))
where
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 Lösung nden Sie auf der Veranstaltungsseite.
5
3
(10+10=20 Punkte)
Sortieren und Laufzeit
3.1 Sortieren durch Einfügen
(10 Teilpunkte)
Implementieren Sie den Insertion-Sort-Algorithmus.
Lösung
isort :: [Integer] -> [Integer]
isort []
= []
isort (x:xs) = ins x (isort xs)
where
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.
Lösung
Die Funktion isort hat eine Laufzeit von O(n2 ). 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) = n2 − n ∈ O(n2 ).
6
4
(10+10=20 Punkte)
Strukturelle Induktion
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
Lösung
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)
<==> (Blatt x)
= (Blatt x)
-- mapT.1
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
d.h.
<==>
<==>
<==>
mapTree
mapTree
(Knoten
(Knoten
(Knoten
id Baum[t<=(n+1)]
id (Knoten lB rB)
= id (Knoten
(mapTree id lB) (mapTree id rB)) = id (Knoten
(id lB) (id rB))
= id (Knoten
(lB) (rB))
= (Knoten lB
lB rB)
lB rB)
lB rB)
rB)
-- mapT.2
-- nach IV
Es wurde mit struktureller Induktion gezeigt, dass die Behauptung gilt.
7
4.2 Listen
(10 Teilpunkte)
Seien folgende Gleichungen gegeben:
++.1
++.2
:
[] ++ ys
: (x:xs) ++ ys
= 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
Lösung
Es folgt ein Beweis mit struktureller Induktion.
Induktionsbasis: xs = []
reverse (reverse []) = []
<==> reverse [] = []
<==> [] = []
rev.1
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)
<==> reverse (reverse xs ++ [x])
= (x:xs)
<==> reverse [x]
++ reverse (reverse xs)
= (x:xs)
<==> reverse (x:[]) ++ reverse (reverse xs)
= (x:xs)
<==> (reverse [] ++ [x]) ++ reverse (reverse xs) = (x:xs)
<==> ([] ++ [x]) ++ reverse (reverse xs)
= (x:xs)
<==> [x] ++ reverse (reverse xs)
= (x:xs)
<==> [x] ++ xs
= (x:xs)
<==> x:[] ++ xs
= (x:xs)
<==> x:([] ++ xs)
= (x:xs)
<==> (x:xs)
= (x:xs)
rev.2
rev.3
rev.2
rev.1
++.1
nach IV
++.2
++.1
Es wurde mit struktureller Induktion gezeigt, dass die Behauptung gilt.
8
5
(10 Punkte)
Primitive Rekursion
Rekursionsschema:
R(0, x1 , ..., xm ) = g(x1 , ..., xm )
R(S(n), x1 , ..., xm ) = h(R(n, x1 , ..., xm ), n, x1 , ..., xm )
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.
Lösung
not 0 = one
not n = Z (not (n-1))
where
one = S Z
isOdd 0 = Z
isOdd n = not (isOdd (n-1))
6
λ
- Kalkül
6.1 Freie und gebundene Ausdrücke
(5+5+10=20 Punkte)
(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))))
Lösung
(λab.(λde.(λc.(λf g.f gc))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)
Lösung
Es gibt 2 Möglichkeiten das Problem zu lösen:
Elegante Lösung:
Z
1
F
z
}|
{ z }| { z }| {
(λa.a (λab.b) (λa.a(λab.b)(λab.a)) (λab.b)) (λab.ab) = (λab.b)
| {z } |
{z
} | {z }
¬
F
F
Normale Lösung:
(λ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)
Lösung
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))))
{z
}
| {z } |{z} |
if
then
else
10
Zugehörige Unterlagen
Herunterladen