diff options
| -rw-r--r-- | ws2015/fortgeschrittene_funktionale_programmierung/blaetter/02/FFP_U02_Typklassen.hs | 206 |
1 files changed, 206 insertions, 0 deletions
diff --git a/ws2015/fortgeschrittene_funktionale_programmierung/blaetter/02/FFP_U02_Typklassen.hs b/ws2015/fortgeschrittene_funktionale_programmierung/blaetter/02/FFP_U02_Typklassen.hs new file mode 100644 index 0000000..5f2d936 --- /dev/null +++ b/ws2015/fortgeschrittene_funktionale_programmierung/blaetter/02/FFP_U02_Typklassen.hs | |||
| @@ -0,0 +1,206 @@ | |||
| 1 | -- Fortgeschrittene Funktionale Programmierung, | ||
| 2 | -- LMU, TCS, Wintersemester 2015/16 | ||
| 3 | -- Steffen Jost, Alexander Isenko | ||
| 4 | -- | ||
| 5 | -- Übungsblatt 02. 28.10.2015 | ||
| 6 | -- | ||
| 7 | -- Thema: | ||
| 8 | -- | ||
| 9 | -- Anweisung: | ||
| 10 | -- Gehen Sie diese Datei durch und bearbeiten Sie | ||
| 11 | -- alle Vorkommen von undefined bzw. die mit -- !!! TODO !!! | ||
| 12 | -- markierten Stellen. Testen Sie Ihre Lösungen mit GHCi! | ||
| 13 | |||
| 14 | |||
| 15 | -- | A2-1 Funktionsdefinitionen | ||
| 16 | -- | ||
| 17 | -- Implementieren Sie folgende grundlegenden, | ||
| 18 | -- bekannten Funktionen in Haskell. | ||
| 19 | -- Selbst wenn Sie die Funktion nicht kennen, | ||
| 20 | -- sollte Ihnen der Typ die korrekte Lösung ermöglichen! | ||
| 21 | -- | ||
| 22 | |||
| 23 | import Prelude hiding (uncurry,flip,(.),map,zip,zipWith,zip,foldl) | ||
| 24 | |||
| 25 | import qualified Data.Map as P | ||
| 26 | |||
| 27 | -- Hinweis: Das import-Statement müssen Sie jetzt noch nicht verstehen, | ||
| 28 | -- es ist nur notwendig zur Vermeidung von Namenskonflikten mit der | ||
| 29 | -- Standardbibliothek, welche die meisten dieser Funktionen bereits enthält. | ||
| 30 | |||
| 31 | -- a) Uncurrying | ||
| 32 | -- > uncurry (/) (1,2) == 0.5 | ||
| 33 | uncurry :: (a -> b -> c) -> ((a,b) -> c) | ||
| 34 | uncurry f (a,b) = f a b | ||
| 35 | |||
| 36 | -- b) Anwendung einer Funktion mit zwei Argumenten auf ein Paar | ||
| 37 | -- > (1,2) ||> (/) == 0.5 | ||
| 38 | (||>) :: (a,b) -> (a -> b -> c) -> c | ||
| 39 | p ||> f = uncurry f p | ||
| 40 | |||
| 41 | |||
| 42 | -- c) Vertauschung der Reihenfolge der Funktionsargumente | ||
| 43 | -- > flip (/) 2 1 == 0.5 | ||
| 44 | flip :: (a -> b -> c) -> (b -> a -> c) | ||
| 45 | flip f b a = f a b | ||
| 46 | |||
| 47 | |||
| 48 | -- d) Funktionskomposition | ||
| 49 | -- > ((\x->x+3) . (\y->y*2)) 1 == 5 | ||
| 50 | (.) :: (b -> c) -> (a -> b) -> a -> c | ||
| 51 | (.) f g x = f $ g x | ||
| 52 | |||
| 53 | |||
| 54 | -- e) Map (im Gegensatz zu A1-3 dieses Mal ohne List-Comprehension) | ||
| 55 | -- > map (+10) [1,2,3,4] == [11,12,13,14] | ||
| 56 | map :: (a -> b) -> [a] -> [b] | ||
| 57 | map _ [] = [] | ||
| 58 | map f (x:xs) = f x : map f xs | ||
| 59 | |||
| 60 | |||
| 61 | -- f) zip: | ||
| 62 | -- > zip ['a','b','c'] [1,2,3,4,5] = [('a',1),('b',2),('c',3)] | ||
| 63 | zip :: [a] -> [b] -> [(a,b)] | ||
| 64 | zip [] _ = [] | ||
| 65 | zip _ [] = [] | ||
| 66 | zip (x:xs) (y:ys) = (x, y) : zip xs ys | ||
| 67 | |||
| 68 | |||
| 69 | -- g) Zippen mit Funktionsanwendung: | ||
| 70 | -- > zipWith (+) [1..] [1..3] == [2,4,6] | ||
| 71 | zipWith :: (a -> b -> c) -> [a] -> [b] -> [c] | ||
| 72 | zipWith f xs ys = map (uncurry f) $ zip xs ys | ||
| 73 | |||
| 74 | |||
| 75 | -- h) Falten nach links: | ||
| 76 | -- > foldl (flip (:) ) [] [1..3] == [3,2,1] | ||
| 77 | -- > foldl (\acc x -> x : acc)) [] [1..3] == [3,2,1] | ||
| 78 | foldl :: (b -> a -> b) -> b -> [a] -> b | ||
| 79 | foldl _ x [] = x | ||
| 80 | foldl f x (y:ys) = foldl f (f x y) ys | ||
| 81 | |||
| 82 | |||
| 83 | |||
| 84 | -- | A2-2 LambdaTerme | ||
| 85 | -- | ||
| 86 | -- Betrachten Sie die Lösung zur A1-1 g): | ||
| 87 | |||
| 88 | data LambdaTerm = LVar Char | ||
| 89 | | LAbs Char LambdaTerm | ||
| 90 | | LApp LambdaTerm LambdaTerm | ||
| 91 | |||
| 92 | -- Ein paar Lambda Terme zum Testen: | ||
| 93 | lTerm_x = LVar 'x' | ||
| 94 | lTerm_y = LVar 'y' | ||
| 95 | lTerm_id = LAbs 'x' lTerm_x | ||
| 96 | lTerm_xx = LApp lTerm_x lTerm_x | ||
| 97 | lTerm_t = LAbs 'x' $ LApp lTerm_y lTerm_xx | ||
| 98 | lTerm_yk = LAbs 'y' $ LApp lTerm_t lTerm_t | ||
| 99 | |||
| 100 | -- a) Implementieren Sie eine Eq-Instanz für den Datentyp LambdaTerm! | ||
| 101 | -- | ||
| 102 | -- (Wer Lambda-Kalkül kennt: Zur Vereinfachung der Aufgabe | ||
| 103 | -- ignorieren wir die übliche alpha-Äquivalenz, d.h. | ||
| 104 | -- (LAbs 'x' $ LVar 'x') und (LAbs 'y' $ LVar 'y') | ||
| 105 | -- dürfen als verschieden betrachtet werden) | ||
| 106 | |||
| 107 | instance Eq LambdaTerm where | ||
| 108 | (LVar a) == (LVar b) = a == b | ||
| 109 | (LVar _) == _ = False | ||
| 110 | (LAbs a t_a) == (LAbs b t_b) = a == b && t_a == t_b | ||
| 111 | (LAbs _ _) == _ = False | ||
| 112 | (LApp t_a t'_a) == (LApp t_b t'_b) = t_a == t_b && t'_a == t'_b | ||
| 113 | (LApp _ _) == _ = False | ||
| 114 | |||
| 115 | -- b) Implementieren Sie die eine Show-Instanz für LambdaTerm. | ||
| 116 | -- Achten Sie dabei auf eine korrekte Klammerung, aber | ||
| 117 | -- verschwenden Sie erst einmal keine Zeit darauf, | ||
| 118 | -- überflüssige Klammern zu vermeiden. | ||
| 119 | |||
| 120 | instance Show LambdaTerm where | ||
| 121 | show (LVar a) = "LVar '" ++ pure a ++ "'" | ||
| 122 | show (LAbs a t) = "LAbs '" ++ pure a ++ "' (" ++ show t ++ ")" | ||
| 123 | show (LApp t t') = "LApp (" ++ show t ++ ") (" ++ show t' ++ ")" | ||
| 124 | |||
| 125 | |||
| 126 | -- | A2-3 Klassendeklaration | ||
| 127 | -- | ||
| 128 | -- a) Deklarieren Sie eine Klasse "FinMap" für endliche partielle Abbildungen, welche folgende Operationen bereitsstellt: | ||
| 129 | -- 1) "emptyM" liefert eine leere Abbildung | ||
| 130 | -- 2) "insertM x a m" fügt die Abbildung [x->a] in die Abbildung m ein. | ||
| 131 | -- Falls x schon enthalten war, dann wird es überschrieben. | ||
| 132 | -- 3) "lookupM x m" liefert Nothing zurück, falls x nicht in m enthalten ist; | ||
| 133 | -- ansonsten wird der für x gespeicherte Wert z zurückgeliefert (in Just verpackt) | ||
| 134 | -- Die Funktion "lookupM" darf dabei annehmen, dass für x eine Vergleichsoperation vorliegt!?! | ||
| 135 | |||
| 136 | class FinMap m where | ||
| 137 | emptyM :: m k v | ||
| 138 | insertM :: Ord k => k -> v -> m k v -> m k v | ||
| 139 | lookupM :: Ord k => k -> m k v -> Maybe v | ||
| 140 | -- Can we get around using constraints here without using the MultiParamTypeClasses language extension? | ||
| 141 | |||
| 142 | -- b) Machen Sie folgenden Datentyp zu einer Instanz der Typklasse Map: | ||
| 143 | |||
| 144 | -- data AL a b = AL [(a,b)] | ||
| 145 | newtype AL a b = AL [(a,b)] -- Äquivalent zur vorherigen Zeile. | ||
| 146 | -- newtype kann und darf verwenden werden, | ||
| 147 | -- wenn man nur einen Konstruktor mit nur einem Argument hat. | ||
| 148 | -- Dies erlaubt GHC eine Optimierung durchzuführen. | ||
| 149 | deriving (Show, Read, Eq) | ||
| 150 | |||
| 151 | instance FinMap AL where | ||
| 152 | emptyM = AL $ [] | ||
| 153 | insertM x a (AL m) = AL $ (x, a) : [p | p@(x', _) <- m, x' /= x] | ||
| 154 | lookupM x (AL m) = listToMaybe [y | (x', y) <- m, x' == x] -- Due to lazyness this is no slower than explicitly short-circuiting the search | ||
| 155 | where | ||
| 156 | -- This is part of Data.Maybe | ||
| 157 | listToMaybe [] = Nothing | ||
| 158 | listToMaybe (x:_) = Just x | ||
| 159 | |||
| 160 | |||
| 161 | ---- Werte zum anschließendem Testen, auskommentieren, | ||
| 162 | ---- sobald Klasse und Instanz definiert wurden: | ||
| 163 | testMap0 :: AL Int Bool | ||
| 164 | testMap0 = emptyM | ||
| 165 | testMap1 = insertM 1 False testMap0 | ||
| 166 | testMap2 = insertM 3 False testMap1 | ||
| 167 | testMap3 = insertM 4 True testMap2 | ||
| 168 | testMap4 = insertM 2 True testMap3 | ||
| 169 | |||
| 170 | |||
| 171 | -- Hinweis: | ||
| 172 | -- Partielle Abbildungen wie hier durch Assoziationslisten zu implementieren, | ||
| 173 | -- ist nicht bensonders effizient, da der Zugriff auf ein Element im Allgemeinen | ||
| 174 | -- den Aufwand O(n) hat (man muss die ganze Liste abklappern - es könnte sich ja | ||
| 175 | -- um das letzte Element der Liste handeln). | ||
| 176 | -- Mit Suchbäumen läßt sich der Aufwand bekanntermaßen auf O(log n) reduzieren. | ||
| 177 | -- Wer noch Lust & Zeit hat, kann versuchen, dies selbst zu implementieren | ||
| 178 | -- und zur einer weiteren Instanz von FinMap machen. | ||
| 179 | -- | ||
| 180 | -- Die Standardbibliothek sieht zur Abstraktion hier keine solche Klasse vor, | ||
| 181 | -- sondern bietet lediglich eine konkrete, effiziente Implementierung von | ||
| 182 | -- endlichen Abbildungen an: Data.Map, welche wir in der kommenden Vorlesung | ||
| 183 | -- betrachten werden. | ||
| 184 | -- | ||
| 185 | -- Dies kann man natürlich ganz schnell zu einer Instanz von FinMap machen. Wie? | ||
| 186 | |||
| 187 | data Map k v = Tip | ||
| 188 | | Branch k v (Map k v) (Map k v) | ||
| 189 | |||
| 190 | instance FinMap Map where | ||
| 191 | emptyM = Tip | ||
| 192 | insertM k v Tip = Branch k v Tip Tip | ||
| 193 | insertM k v (Branch k' v' lt gt) | ||
| 194 | | k == k' = Branch k v lt gt | ||
| 195 | | k < k' = Branch k' v' (insertM k v lt) gt | ||
| 196 | | otherwise = Branch k' v' lt (insertM k v gt) | ||
| 197 | lookupM _ Tip = Nothing | ||
| 198 | lookupM k (Branch k' v' lt gt) | ||
| 199 | | k == k' = Just v' | ||
| 200 | | k < k' = lookupM k lt | ||
| 201 | | otherwise = lookupM k gt | ||
| 202 | |||
| 203 | instance FinMap P.Map where | ||
| 204 | emptyM = P.empty | ||
| 205 | insertM = P.insert | ||
| 206 | lookupM = P.lookup | ||
