diff --git a/.ghci b/.ghci deleted file mode 100644 index 6e48b23..0000000 --- a/.ghci +++ /dev/null @@ -1,29 +0,0 @@ -:def vis \_ -> return $ "GHC.Vis.vis" -:def mvis \_ -> return $ "GHC.Vis.mvis" -:def svis \_ -> return $ "GHC.Vis.svis" -:def view \x -> return $ "GHC.Vis.view (" ++ x ++ ") " ++ show x -:def eval \x -> return $ "GHC.Vis.eval \"" ++ x ++ "\"" -:def switch \_ -> return $ "GHC.Vis.switch" -:def update \_ -> return $ "GHC.Vis.update" -:def clear \_ -> return $ "GHC.Vis.clear" -:def restore \_ -> return $ "GHC.Vis.restore" -:def timeBack \_ -> return $ "GHC.Vis.history (+1)" -:def timeForward \_ -> return $ "GHC.Vis.history (\\x -> x - 1)" -:def export \x -> return $ "GHC.Vis.export " ++ show x -:def setDepth \x -> return $ "GHC.Vis.setDepth (" ++ x ++ ")" - --- Evaluate one step and update values in ghc-vis -:def su \x -> return $ ":step " ++ x ++ "\n:update" - --- Continue to next breakpoint and update values in ghc-vis -:def cu \_ -> return $ ":continue\n:update" - --- Keep evaluating and updating on any key pressed, quit with q -:def asu \x -> getChar >>= \c -> case c of { 'q' -> return ""; _ -> return $ ":step " ++ x ++ "\n:update\n:asu" } - --- :tsu t x --- Evaluate one step of expression x every t seconds until any key is pressed -:def tsu \x -> (System.Timeout.timeout (round $ 1000000 * (read . head $ words x :: Double)) getChar) >>= \c -> case c of { Just _ -> return ""; _ -> return $ ":step " ++ (unwords . tail $ words x) ++ "\n:update\n:tsu " ++ (head $ words x) } - -:set -fobject-code - diff --git a/app/Aufgabe1Main.hs b/app/Aufgabe1Main.hs new file mode 100644 index 0000000..a8a86ee --- /dev/null +++ b/app/Aufgabe1Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Aufgabe1 + +main :: IO () +main = putStrLn result diff --git a/app/Aufgabe2Main.hs b/app/Aufgabe2Main.hs new file mode 100644 index 0000000..19eac74 --- /dev/null +++ b/app/Aufgabe2Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Aufgabe2 + +main :: IO () +main = putStrLn result diff --git a/app/Aufgabe3Main.hs b/app/Aufgabe3Main.hs new file mode 100644 index 0000000..8c9213d --- /dev/null +++ b/app/Aufgabe3Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Aufgabe3 + +main :: IO () +main = putStrLn result diff --git a/install_dependencies.sh b/install_dependencies.sh deleted file mode 100644 index 45510ed..0000000 --- a/install_dependencies.sh +++ /dev/null @@ -1,2 +0,0 @@ -tar xf /media/remote/ygottschalk/.stack.tar.gz -C $HOME/ - diff --git a/src/AreaCode.hs b/src/AreaCode.hs new file mode 100644 index 0000000..3e72087 --- /dev/null +++ b/src/AreaCode.hs @@ -0,0 +1,27 @@ +module AreaCode (AreaCode,separateAreaCode) where + +import qualified Data.Attoparsec.Text as A +import Data.Attoparsec.Combinator +import qualified Data.Text as T + +type Number = String +type Name = String +type AreaCode = String + +{-Area Code Parser-} + +separateAreaCode :: Number -> (AreaCode,Number) +separateAreaCode nr = + case A.parse parseAreaCode (T.pack nr) of + A.Done s ac -> (ac, T.unpack s) + _ -> ("",nr) + +parseAreaCode :: A.Parser String +parseAreaCode = do + many' (A.char ' ') + A.char '(' + ac <- many1 $ A.satisfy (A.inClass "0123456789") + A.char ')' + many' (A.char ' ') + return ac + diff --git a/src/Aufgabe1.hs b/src/Aufgabe1.hs new file mode 100644 index 0000000..fa310ea --- /dev/null +++ b/src/Aufgabe1.hs @@ -0,0 +1,82 @@ +-- Aufgabe 1 +-- ========= + +module Aufgabe1 where + +-- Functional phone book – Implementieren Sie ein Telefonbuch als Funktion. + +type Number = String +type Name = String +type Entry = [Number] +newtype PhoneBook = PB (Name -> Entry) + + +-- Implementieren Sie die Funktion `usePB`, welche aus einem `PhoneBook` +-- mit einem `Name` den zugehörigen `Entry` findet` + + +usePB :: PhoneBook -> Name -> Entry +usePB = undefined + + + +-- Implementieren Sie eine `Monoid` Instanz für `PhoneBook`, um zu garantieren, +-- dass `PhoneBook` ein leeres Element hat und eine Verkettungsfunktion, die +-- zwei `PhoneBook`s in eines zusammenführt. + + +instance Monoid PhoneBook where + mempty = undefined + mappend = undefined + + + +-- Implementieren Sie eine Funktion `addEntry`, welche einem `PhoneBook` eine +-- `Name` zu `Number` Verknüpfung hinzufügt, also für einen gegebenen Namen und +-- eine Nummer einen Eintrag im Telefonbuch erstellt. + + +addEntry :: Name -> Number -> PhoneBook -> PhoneBook +addEntry = undefined + + + +-- Implementieren Sie eine Funktion `delEntry`, die alle Nummern aus dem `PhoneBook` +-- entfernt, die mit dem gegebenen `Name` assoziiert sind. +-- Hinweis: "Entfernt" heißt streng genommen nur, dass die Nummern nicht mehr aus +-- dem resultierenden Telefonbuch herausgesucht werden können + + +delEntry :: Name -> PhoneBook -> PhoneBook +delEntry = undefined + + +-- Implementieren Sie eine Funktion `findInMult`, welche alle Einträge aus einer +-- Liste von `PhoneBook`s sucht + + +findInMult :: [PhoneBook] -> Name -> Entry +findInMult = undefined + + +result = "Wie war noch mal die Nummer von diesem Alonzo Church? Vielleicht kann der mir weiterhelfen.. \n" + ++ (show $ findInMult [pb2,pb3] "Alonzo Church") ++ "\n" + where pb1 = addEntry "Alonzo Church" "(0123) 73645362" mempty + pb2 = delEntry "Alonzo Church" pb1 + pb3 = addEntry "Haskell Brooks Curry" "(0167) 987761262" (mappend pb1 pb2) + + + + + + + + + + + + + + + + diff --git a/src/Aufgabe1.lhs b/src/Aufgabe1.lhs new file mode 100644 index 0000000..feab504 --- /dev/null +++ b/src/Aufgabe1.lhs @@ -0,0 +1,72 @@ +Aufgabe 1 +========= + +> module Aufgabe1 where + +Functional phone book – Implementieren Sie ein Telefonbuch als Funktion. + +> type Number = String +> type Name = String +> type Entry = [Number] +> newtype PhoneBook = PB (Name -> Entry) + + +Implementieren Sie die Funktion `usePB`, welche aus einem `PhoneBook` +mit einem `Name` den zugehörigen `Entry` findet` + + +> usePB :: PhoneBook -> Name -> Entry +> usePB = undefined + + + +Implementieren Sie eine `Monoid` Instanz für `PhoneBook`, um zu garantieren, +dass `PhoneBook` ein leeres Element hat und eine Verkettungsfunktion, die +zwei `PhoneBook`s in eines zusammenführt. + + +> instance Monoid PhoneBook where +> mempty = undefined +> mappend = undefined + + + +Implementieren Sie eine Funktion `addEntry`, welche einem `PhoneBook` eine +`Name` zu `Number` Verknüpfung hinzufügt, also für einen gegebenen Namen und +eine Nummer einen Eintrag im Telefonbuch erstellt. + + +> addEntry :: Name -> Number -> PhoneBook -> PhoneBook +> addEntry = undefined + + + +Implementieren Sie eine Funktion `delEntry`, die alle Nummern aus dem `PhoneBook` +entfernt, die mit dem gegebenen `Name` assoziiert sind. +Hinweis: "Entfernt" heißt streng genommen nur, dass die Nummern nicht mehr aus +dem resultierenden Telefonbuch herausgesucht werden können + + +> delEntry :: Name -> PhoneBook -> PhoneBook +> delEntry = undefined + + + +Implementieren Sie eine Funktion `findInMult`, welche alle Einträge aus einer +Liste von `PhoneBook`s sucht + + +> findInMult :: [PhoneBook] -> Name -> Entry +> findInMult = undefined + + + +> result = "Wie war noch mal die Nummer von diesem Alonzo Church? Vielleicht kann der mir weiterhelfen.. \n" +> ++ (show $ findInMult [pb2,pb3] "Alonzo Church") ++ "\n" +> where pb1 = addEntry "Alonzo Church" "(0123) 73645362" mempty +> pb2 = delEntry "Alonzo Church" pb1 +> pb3 = addEntry "Haskell Brooks Curry" "(0167) 987761262" (mappend pb1 pb2) + + + + diff --git a/src/Aufgabe1.md b/src/Aufgabe1.md new file mode 100644 index 0000000..d6105fe --- /dev/null +++ b/src/Aufgabe1.md @@ -0,0 +1,79 @@ +Aufgabe 1 +========= + +```haskell +module Aufgabe1 where +``` + +Functional phone book – Implementieren Sie ein Telefonbuch als Funktion. + +```haskell +type Number = String +type Name = String +type Entry = [Number] +newtype PhoneBook = PB (Name -> Entry) +``` + +Implementieren Sie die Funktion `usePB`, welche aus einem `PhoneBook` +mit einem `Name` den zugehörigen `Entry` findet` + +```haskell +usePB :: PhoneBook -> Name -> Entry +usePB = undefined +``` + + +Implementieren Sie eine `Monoid` Instanz für `PhoneBook`, um zu garantieren, +dass `PhoneBook` ein leeres Element hat und eine Verkettungsfunktion, die +zwei `PhoneBook`s in eines zusammenführt. + +```haskell +instance Monoid PhoneBook where + mempty = undefined + mappend pb1 pb2 = undefined +``` + + +Implementieren Sie eine Funktion `addEntry`, welche einem `PhoneBook` eine +`Name` zu `Number` Verknüpfung hinzufügt, also für einen gegebenen Namen und +eine Nummer einen Eintrag im Telefonbuch erstellt. + +```haskell +addEntry :: Name -> Number -> PhoneBook -> PhoneBook +addEntry n nr pb = undefined +``` + + +Implementieren Sie eine Funktion `delEntry`, die alle Nummern aus dem `PhoneBook` +entfernt, die mit dem gegebenen `Name` assoziiert sind. +Hinweis: "Entfernt" heißt streng genommen nur, dass die Nummern nicht mehr aus +dem resultierenden Telefonbuch herausgesucht werden können + +```haskell +delEntry :: Name -> PhoneBook -> PhoneBook +delEntry n pb = undefined +``` + + +Implementieren Sie eine Funktion `findInMult`, welche alle Einträge aus einer +Liste von `PhoneBook`s sucht + +```haskell +findInMult :: [PhoneBook] -> Name -> Entry +findInMult = undefined +``` + +```haskell +result = "Wie war noch mal die Nummer von diesem Alonzo Church? Vielleicht kann der mir weiterhelfen.. \n" + ++ (show $ findInMult [pb2,pb3] "Alonzo Church") ++ "\n" + where pb1 = addEntry "Alonzo Church" "(0123) 73645362" mempty + pb2 = delEntry "Alonzo Church" pb1 + pb3 = addEntry "Haskell Brooks Curry" "(0167) 987761262" (mappend pb1 pb2) +``` + + + + + + + diff --git a/src/Aufgabe2.hs b/src/Aufgabe2.hs new file mode 100644 index 0000000..e0bd4ef --- /dev/null +++ b/src/Aufgabe2.hs @@ -0,0 +1,41 @@ +-- Aufgabe 2 +-- ========= + +module Aufgabe2 where + +-- Besser und allgemeiner +-- ---------------------- + +-- Vereinfachen und verallgemeinern sie folgenden Ausdrücke so weit wie +-- möglich. Geben Sie die dadurch entstehenden Typsignaturen und +-- Funktionsdefinitionen an. Bedenken Sie, dass wenn sie auf eine Typklasse +-- abstrahieren, Sie die gesamten Gesetze der Typklasse benutzen können. + +-- Kann die Funktion nachher mehr als vorher? + +-- *Bonus*: Hat sich an der Laufzeit etwas verändert? + + +mystery1 :: [[a]] -> [[a]] +mystery1 = map (++[]) + +mystery2 :: (Int -> Bool) + -> Maybe (Either String Int) + -> Maybe (Either String Bool) +mystery2 f (Just (Right a)) = Just . Right . f $ a +mystery2 _ (Just (Left b)) = Just (Left b) +mystery2 _ Nothing = Nothing + +mystery3 :: (Eq a) => a -> a -> a -> Bool +mystery3 x y z + | y == z = True + | z == y && y == x = True + | x /= z = False + | y /= x = False + | z /= y || y /= x = True + | otherwise = False + +result = "foo?" + + + diff --git a/src/Aufgabe2.lhs b/src/Aufgabe2.lhs new file mode 100644 index 0000000..b61cafb --- /dev/null +++ b/src/Aufgabe2.lhs @@ -0,0 +1,45 @@ +Aufgabe 2 +========= + +> module Aufgabe2 where + +Besser und allgemeiner +---------------------- + +Vereinfachen und verallgemeinern sie folgenden Ausdrücke so weit wie +möglich. Geben Sie die dadurch entstehenden Typsignaturen und +Funktionsdefinitionen an. Bedenken Sie, dass wenn sie auf eine Typklasse +abstrahieren, Sie die gesamten Gesetze der Typklasse benutzen können. + +Kann die Funktion nachher mehr als vorher? + +*Bonus*: Hat sich an der Laufzeit etwas verändert? + + +> mystery1 :: [[a]] -> [[a]] +> mystery1 = map (++[]) + + +> mystery2 :: (Int -> Bool) +> -> Maybe (Either String Int) +> -> Maybe (Either String Bool) +> mystery2 f (Just (Right a)) = Just . Right . f $ a +> mystery2 _ (Just (Left b)) = Just (Left b) +> mystery2 _ Nothing = Nothing + + +> mystery3 :: (Eq a) => a -> a -> a -> Bool +> mystery3 x y z +> | y == z = True +> | z == y && y == x = True +> | x /= z = False +> | y /= x = False +> | z /= y || y /= x = True +> | otherwise = False + + + +> result = "foo?" + + + diff --git a/src/Aufgabe2.md b/src/Aufgabe2.md new file mode 100644 index 0000000..5a1df0b --- /dev/null +++ b/src/Aufgabe2.md @@ -0,0 +1,47 @@ +Aufgabe 2 +========= + +```haskell +module Aufgabe2 where +``` + +Besser und allgemeiner +---------------------- + +Vereinfachen und verallgemeinern sie folgenden Ausdrücke so weit wie +möglich. Geben Sie die dadurch entstehenden Typsignaturen und +Funktionsdefinitionen an. Bedenken Sie, dass wenn sie auf eine Typklasse +abstrahieren, Sie die gesamten Gesetze der Typklasse benutzen können. + +Kann die Funktion nachher mehr als vorher? + +*Bonus*: Hat sich an der Laufzeit etwas verändert? + +```haskell +mystery1 :: [[a]] -> [[a]] +mystery1 = map (++[]) + + +mystery2 :: (Int -> Bool) + -> Maybe (Either String Int) + -> Maybe (Either String Bool) +mystery2 f (Just (Right a)) = Just . Right . f $ a +mystery2 _ (Just (Left b)) = Just (Left b) +mystery2 _ Nothing = Nothing + + +mystery3 :: (Eq a) => a -> a -> a -> Bool +mystery3 x y z + | y == z = True + | z == y && y == x = True + | x /= z = False + | y /= x = False + | z /= y || y /= x = True + | otherwise = False + + + +result = "foo?" +``` + + diff --git a/src/Aufgabe3.hs b/src/Aufgabe3.hs new file mode 100644 index 0000000..66f180c --- /dev/null +++ b/src/Aufgabe3.hs @@ -0,0 +1,147 @@ +-- Aufgabe 3 +-- ========= + +module Aufgabe3 where + +import FunPB +import DataPB +import AreaCode +import Data.Char + +-- Functor – ein Container? +-- -------------------------- + +-- Zur Erinnerung: + + +-- class Functor f where +-- fmap :: (a -> b) -> f a -> f b + +-- Aus der Vorlesung 2 kennen Sie bereits die `Functor`-Instanzen für `[]`, `Identity` +-- und einen binären Baum. Außerdem haben Sie gelernt, dass Sie auch für Ihre eigenen +-- Typen `Functor`-Instanzen definieren können. Eine Intuition dafür, ob sich für einen +-- Typ eine `Functor`-Instanz schreiben lässt, erhalten Sie, indem Sie sich fragen, ob +-- der Typ "eine Art Container" ist, auf deren Inhalte sich Funktionen (a -> b) anwenden +-- lassen. Was bedeutet dies in Haskell-Syntax? Hierfür ist es dienlich, sich den +-- `type constructor` des betreffenden Datentyps anzuschauen. Handelt es sich um einen +-- polymorphen Datentyp stehen die Chancen gut, dass es ein `Functor` ist. + +-- data Bool = False | True + +-- data Maybe a = Nothing | Just a + +-- data (,) a b = (a,b) + +-- `Bool` ist offenbar kein `Functor`, denn der Typkonstruktor `Bool` hat keine Parameter – +-- hier ist nur Platz für True und False. `Maybe a` dagegen hat einen Parameter, einen freien +-- Slot für alles mögliche. Der Tupeltyp `(,) a b` hat sogar zwei Parameter – er kann Dinge von +-- zwei verschiedenen Typen enthalten. Hier stellt sich die Frage, für welchen Container die +-- Functorinstanz definiert ist. Ähnlich wie Funktionen, lassen sich auch Typkonstruktoren +-- partiell anwenden. Für die Instanziierung werden dem Typkonstruktor daher alle bis auf ein +-- Parameter übergeben. Dieser letzte, freie Parameter legt dann den Inhalt des "Container" +-- fest. Beispiel: + +-- instance Functor ((,) a) where --Die Functor-Instanz ist für (,) a definiert +-- fmap f (x,y) = (x, f y) --Also wird über Tupelslot 2 "gemapt" + +-- Implementieren Sie `Functor`-Instanzen für die folgenden Datentypen: + + +data Vielleicht a = Nichts | Etwas a + deriving (Show,Eq) + +instance Functor Vielleicht where + fmap = undefined + + +data Entweder a b = Jenes a | Dieses b + deriving (Show,Eq) + +instance Functor (Entweder a) where + fmap = undefined + + +data Konstant a b = Konstant a + deriving (Show,Eq) + +instance Functor (Konstant a) where + fmap = undefined + + +-- Achtung: Die "Container"-Metapher hat ihre Grenzen. Betrachten Sie hierzu noch einmal den +-- Datentyp Pred a von Zettel 1: `data Pred a = Pred (\a -> Bool)`. + +data Pred a = Pred (a -> Bool) +runPred :: Pred a -> (a -> Bool) +runPred (Pred p) = p + +-- `Pred a` macht den Anschein als handele es sich auch hier um einen Container mit Inhalt a. +-- Trotzdem lässt sich `Functor` hierfür nicht instanziieren. Der Unterschied liegt darin, +-- dass der Typaramter `a` als Input und nicht als Ergebnis in der vom Konstruktor `Pred` +-- "eingepackten" Berechnung auftaucht. Allerdings lässt sich hier auf eine sehr ähnliche +-- Eigenschaft abstrahieren, die wir für's Erste `InputFunctor` nennen wollen. + +class InputFunctor f where + inputmap :: (a -> b) -> f b -> f a + +-- Schreiben Sie eine `InputFunctor`-Instanz für `Pred a`. + +instance InputFunctor Pred where + inputmap = undefined + +-- Hiermit lässt sich nun bequem die folgende Funktion definieren, welche aus einem +-- `Pred Int` ein `Pred String` macht, das prüft, ob ein Eingabestring wenigstens die Länge 5 hat. + +atLeast5 :: Pred Int +atLeast5 = Pred $ (\x -> x>=5) + +atLeast5Char :: Pred String +atLeast5Char = inputmap length atLeast5 + +-- Functorial phone book +-- --------------------- + +-- Jetzt noch einmal zurück zu PhoneBook aus Aufgabe 1. + +type Number = String +type Name = String +type Entry = [Number] + +newtype PhoneBook = PB (Name -> Entry) + +-- `PhoneBook` hat keinen Parameter, aber die allgemeinere Version `FunPB` hat sogar zwei: +-- data FunPB a b = FunPB (a -> (a,[b])) + +-- Beachten Sie, dass sich auch der Rückgabetyp ein wenig unterscheidet. Die Idee ist, +-- dass FunPB zusätzlich zu den assoziierten `Number`s (angenommen `b` ist `Number`) auch +-- den gesuchten `Name` (angenommen `a` ist `Name`) zurückgibt. + +-- Implementieren Sie eine `Functor`-Instanz für `FunPB`. +-- Hinweis: Sie können benutzen, dass für `[]`, `((,) a)` und sogar für den "function arrow" +-- `((->) a)` bereits `Functor`-Instanzen in der [`GHC.Base`](https://hackage.haskell.org/package/base "GHC.Base") existieren. + +instance Functor (FunPB a) where + fmap = undefined + +-- Die Functor-Instanz erlaubt uns nun die Funktionen +-- `separateAreaCode :: Number -> (AreaCode,Number)` und `snd` zu verwenden, um ein +-- TelefonBuch mit separiertem bzw. ganz ohne AreaCode zu erhalten. + +areaCodeFunPB :: FunPB Name String -> FunPB Name (AreaCode,Number) +areaCodeFunPB = fmap separateAreaCode + +withoutAreaCodeFunPB :: FunPB Name Number -> FunPB Name Number +withoutAreaCodeFunPB = fmap (snd.separateAreaCode) + + +result = "Suche \"Paula\" in (FunPB Name Number): \n" + ++ (show $ runFunPB (dataToFunPB simpleData) "Paula") ++ "\n" + ++ "Suche \"Paula\" in (FunPB Name (AreaCode,Number)): \n" + ++ (show $ runFunPB (areaCodeFunPB $ dataToFunPB simpleData) "Paula") ++ "\n" + ++ "Suche \"Paula\" in (FunPB Name Number) ohne Vorwahl: \n" + ++ (show $ runFunPB (withoutAreaCodeFunPB $ dataToFunPB simpleData) "Paula") + + + + + diff --git a/src/Aufgabe3.lhs b/src/Aufgabe3.lhs new file mode 100644 index 0000000..8274047 --- /dev/null +++ b/src/Aufgabe3.lhs @@ -0,0 +1,168 @@ +Aufgabe 3 +========= + +> module Aufgabe3 where + +> import FunPB +> import DataPB +> import AreaCode +> import Data.Char + +`Functor` – ein Container? +-------------------------- + +Zur Erinnerung: + +class Functor f where + fmap :: (a -> b) -> f a -> f b + +Aus der Vorlesung 2 kennen Sie bereits die `Functor`-Instanzen für `[]`, `Identity` +und einen binären Baum. Außerdem haben Sie gelernt, dass Sie auch für Ihre eigenen +Typen `Functor`-Instanzen definieren können. Eine Intuition dafür, ob sich für einen +Typ eine `Functor`-Instanz schreiben lässt, erhalten Sie, indem Sie sich fragen, ob +der Typ "eine Art Container" ist, auf deren Inhalte sich Funktionen (a -> b) anwenden +lassen. Was bedeutet dies in Haskell-Syntax? Hierfür ist es dienlich, sich den +`type constructor` des betreffenden Datentyps anzuschauen. Handelt es sich um einen +polymorphen Datentyp stehen die Chancen gut, dass es ein `Functor` ist. + + +data Bool = False | True + +data Maybe a = Nothing | Just a + +data (,) a b = (a,b) + + +`Bool` ist offenbar kein `Functor`, denn der Typkonstruktor `Bool` hat keine Parameter – +hier ist nur Platz für True und False. `Maybe a` dagegen hat einen Parameter, einen freien +Slot für alles mögliche. Der Tupeltyp `(,) a b` hat sogar zwei Parameter – er kann Dinge von +zwei verschiedenen Typen enthalten. Hier stellt sich die Frage, für welchen Container die +Functorinstanz definiert ist. Ähnlich wie Funktionen, lassen sich auch Typkonstruktoren +partiell anwenden. Für die Instanziierung werden dem Typkonstruktor daher alle bis auf ein +Parameter übergeben. Dieser letzte, freie Parameter legt dann den Inhalt des "Container" +fest. Beispiel: + + +instance Functor ((,) a) where --Die Functor-Instanz ist für (,) a definiert + fmap f (x,y) = (x, f y) --Also wird über Tupelslot 2 "gemapt" + + +Implementieren Sie `Functor`-Instanzen für die folgenden Datentypen: + + +> data Vielleicht a = Nichts | Etwas a +> deriving (Show,Eq) + +> instance Functor Vielleicht where +> fmap = undefined + + +> data Entweder a b = Jenes a | Dieses b +> deriving (Show,Eq) + +> instance Functor (Entweder a) where +> fmap = undefined + + +> data Konstant a b = Konstant a +> deriving (Show,Eq) + +> instance Functor (Konstant a) where +> fmap = undefined + + + +Achtung: Die "Container"-Metapher hat ihre Grenzen. Betrachten Sie hierzu noch einmal den +Datentyp Pred a von Zettel 1: `data Pred a = Pred (\a -> Bool)`. + + +> data Pred a = Pred (a -> Bool) +> runPred :: Pred a -> (a -> Bool) +> runPred (Pred p) = p + + +`Pred a` macht den Anschein als handele es sich auch hier um einen Container mit Inhalt a. +Trotzdem lässt sich `Functor` hierfür nicht instanziieren. Der Unterschied liegt darin, +dass der Typaramter `a` als Input und nicht als Ergebnis in der vom Konstruktor `Pred` +"eingepackten" Berechnung auftaucht. Allerdings lässt sich hier auf eine sehr ähnliche +Eigenschaft abstrahieren, die wir für's Erste `InputFunctor` nennen wollen. +``` + +> class InputFunctor f where +> inputmap :: (a -> b) -> f b -> f a + + +Schreiben Sie eine `InputFunctor`-Instanz für `Pred a`. + + +> instance InputFunctor Pred where +> inputmap = undefined + + +Hiermit lässt sich nun bequem die folgende Funktion definieren, welche aus einem +`Pred Int` ein `Pred String` macht, das prüft, ob ein Eingabestring wenigstens die Länge 5 hat. + + +> atLeast5 :: Pred Int +> atLeast5 = Pred $ (\x -> x>=5) + +> atLeast5Char :: Pred String +> atLeast5Char = inputmap length atLeast5 + + +Functorial phone book +--------------------- + +Jetzt noch einmal zurück zu PhoneBook aus Aufgabe 1. + + +> type Number = String +> type Name = String +> type Entry = [Number] + +> newtype PhoneBook = PB (Name -> Entry) + + +`PhoneBook` hat keinen Parameter, aber die allgemeinere Version `FunPB` hat sogar zwei: + + + data FunPB a b = FunPB (a -> (a,[b])) + + +Beachten Sie, dass sich auch der Rückgabetyp ein wenig unterscheidet. Die Idee ist, +dass FunPB zusätzlich zu den assoziierten `Number`s (angenommen `b` ist `Number`) auch +den gesuchten `Name` (angenommen `a` ist `Name`) zurückgibt. + +Implementieren Sie eine `Functor`-Instanz für `FunPB`. +Hinweis: Sie können benutzen, dass für `[]`, `((,) a)` und sogar für den "function arrow" +`((->) a)` bereits `Functor`-Instanzen in der [`GHC.Base`](https://hackage.haskell.org/package/base "GHC.Base") existieren. + + +> instance Functor (FunPB a) where +> fmap = undefined + + +Die Functor-Instanz erlaubt uns nun die Funktionen +`separateAreaCode :: Number -> (AreaCode,Number)` und `snd` zu verwenden, um ein +TelefonBuch mit separiertem bzw. ganz ohne AreaCode zu erhalten. + + +> areaCodeFunPB :: FunPB Name String -> FunPB Name (AreaCode,Number) +> areaCodeFunPB = fmap separateAreaCode + +> withoutAreaCodeFunPB :: FunPB Name Number -> FunPB Name Number +> withoutAreaCodeFunPB = fmap (snd.separateAreaCode) + + + + +> result = "Suche \"Paula\" in (FunPB Name Number): \n" +> ++ (show $ runFunPB (dataToFunPB simpleData) "Paula") ++ "\n" +> ++ "Suche \"Paula\" in (FunPB Name (AreaCode,Number)): \n" +> ++ (show $ runFunPB (areaCodeFunPB $ dataToFunPB simpleData) "Paula") ++ "\n" +> ++ "Suche \"Paula\" in (FunPB Name Number) ohne Vorwahl: \n" +> ++ (show $ runFunPB (withoutAreaCodeFunPB $ dataToFunPB simpleData) "Paula") + + + + diff --git a/src/Aufgabe3.md b/src/Aufgabe3.md new file mode 100644 index 0000000..5b43008 --- /dev/null +++ b/src/Aufgabe3.md @@ -0,0 +1,171 @@ +Aufgabe 3 +========= + +```haskell +module Aufgabe3 where + +import FunPB +import DataPB +import AreaCode +import Data.Char +``` + +`Functor` – ein Container? +-------------------------- + +Zur Erinnerung: + +```haskell +class Functor f where + fmap :: (a -> b) -> f a -> f b +``` + +Aus der Vorlesung 2 kennen Sie bereits die `Functor`-Instanzen für `[]`, `Identity` +und einen binären Baum. Außerdem haben Sie gelernt, dass Sie auch für Ihre eigenen +Typen `Functor`-Instanzen definieren können. Eine Intuition dafür, ob sich für einen +Typ eine `Functor`-Instanz schreiben lässt, erhalten Sie, indem Sie sich fragen, ob +der Typ "eine Art Container" ist, auf deren Inhalte sich Funktionen (a -> b) anwenden +lassen. Was bedeutet dies in Haskell-Syntax? Hierfür ist es dienlich, sich den +`type constructor` des betreffenden Datentyps anzuschauen. Handelt es sich um einen +polymorphen Datentyp stehen die Chancen gut, dass es ein `Functor` ist. + +```haskell +data Bool = False | True + +data Maybe a = Nothing | Just a + +data (,) a b = (a,b) +``` + +`Bool` ist offenbar kein `Functor`, denn der Typkonstruktor `Bool` hat keine Parameter – +hier ist nur Platz für True und False. `Maybe a` dagegen hat einen Parameter, einen freien +Slot für alles mögliche. Der Tupeltyp `(,) a b` hat sogar zwei Parameter – er kann Dinge von +zwei verschiedenen Typen enthalten. Hier stellt sich die Frage, für welchen Container die +Functorinstanz definiert ist. Ähnlich wie Funktionen, lassen sich auch Typkonstruktoren +partiell anwenden. Für die Instanziierung werden dem Typkonstruktor daher alle bis auf ein +Parameter übergeben. Dieser letzte, freie Parameter legt dann den Inhalt des "Container" +fest. Beispiel: + +```haskell +instance Functor ((,) a) where --Die Functor-Instanz ist für (,) a definiert + fmap f (x,y) = (x, f y) --Also wird über Tupelslot 2 "gemapt" +``` + +Implementieren Sie `Functor`-Instanzen für die folgenden Datentypen: + +```haskell +data Vielleicht a = Nichts | Etwas a + deriving (Show,Eq) + +instance Functor Vielleicht where + fmap = undefined + + +data Entweder a b = Jenes a | Dieses b + deriving (Show,Eq) + +instance Functor (Entweder a) where + fmap = undefined + + +data Konstant a b = Konstant a + deriving (Show,Eq) + +instance Functor (Konstant a) where + fmap = undefined +``` + +Achtung: Die "Container"-Metapher hat ihre Grenzen. Betrachten Sie hierzu noch einmal den +Datentyp Pred a von Zettel 1: `data Pred a = Pred (\a -> Bool)`. + +```haskell +data Pred a = Pred (a -> Bool) + runPred :: Pred a -> (a -> Bool) + runPred (Pred p) = p +``` + +`Pred a` macht den Anschein als handele es sich auch hier um einen Container mit Inhalt a. +Trotzdem lässt sich `Functor` hierfür nicht instanziieren. Der Unterschied liegt darin, +dass der Typaramter `a` als Input und nicht als Ergebnis in der vom Konstruktor `Pred` +"eingepackten" Berechnung auftaucht. Allerdings lässt sich hier auf eine sehr ähnliche +Eigenschaft abstrahieren, die wir für's Erste `InputFunctor` nennen wollen. + +```haskell +class InputFunctor f where + inputmap :: (a -> b) -> f b -> f a +``` + +Schreiben Sie eine `InputFunctor`-Instanz für `Pred a`. + +```haskell +instance InputFunctor Pred where + inputmap = undefined +``` + +Hiermit lässt sich nun bequem die folgende Funktion definieren, welche aus einem +`Pred Int` ein `Pred String` macht, das prüft, ob ein Eingabestring wenigstens die Länge 5 hat. + +```haskell +atLeast5 :: Pred Int +atLeast5 = Pred $ (\x -> x>=5) + +atLeast5Char :: Pred String +atLeast5Char = inputmap length atLeast5 +``` + +Functorial phone book +--------------------- + +Jetzt noch einmal zurück zu PhoneBook aus Aufgabe 1. + +```haskell +type Number = String +type Name = String +type Entry = [Number] + +newtype PhoneBook = PB (Name -> Entry) +``` + +`PhoneBook` hat keinen Parameter, aber die allgemeinere Version `FunPB` hat sogar zwei: + +```haskell + data FunPB a b = FunPB (a -> (a,[b])) +``` + +Beachten Sie, dass sich auch der Rückgabetyp ein wenig unterscheidet. Die Idee ist, +dass FunPB zusätzlich zu den assoziierten `Number`s (angenommen `b` ist `Number`) auch +den gesuchten `Name` (angenommen `a` ist `Name`) zurückgibt. + +Implementieren Sie eine `Functor`-Instanz für `FunPB`. +Hinweis: Sie können benutzen, dass für `[]`, `((,) a)` und sogar für den "function arrow" +`((->) a)` bereits `Functor`-Instanzen in der [`GHC.Base`](https://hackage.haskell.org/package/base "GHC.Base") existieren. + +```haskell +instance Functor (FunPB a) where + fmap = undefined +``` + +Die Functor-Instanz erlaubt uns nun die Funktionen +`separateAreaCode :: Number -> (AreaCode,Number)` und `snd` zu verwenden, um ein +TelefonBuch mit separiertem bzw. ganz ohne AreaCode zu erhalten. + +```haskell +areaCodeFunPB :: FunPB Name Number -> FunPB Name (AreaCode,Number) +areaCodeFunPB = fmap separateAreaCode + +withoutAreaCodeFunPB :: FunPB Name Number -> FunPB Name Number +withoutAreaCodeFunPB = fmap (snd.separateAreaCode) +``` + + + +```haskell +result = "Suche \"Paula\" in (FunPB Name Number): \n" + ++ (show $ runFunPB (dataToFunPB simpleData) "Paula") ++ "\n" + ++ "Suche \"Paula\" in (FunPB Name (AreaCode,Number)): \n" + ++ (show $ runFunPB (areaCodeFunPB $ dataToFunPB simpleData) "Paula") ++ "\n" + ++ "Suche \"Paula\" in (FunPB Name Number) ohne Vorwahl: \n" + ++ (show $ runFunPB (withoutAreaCodeFunPB $ dataToFunPB simpleData) "Paula") + +``` + diff --git a/src/Aufgabe4.hs b/src/Aufgabe4.hs index c91a5a4..ea7a72e 100644 --- a/src/Aufgabe4.hs +++ b/src/Aufgabe4.hs @@ -1,85 +1,6 @@ --- Aufgabe 4 --- ========= - module Aufgabe4 where +-- Aufgabe 4 finden befindet sich auf dem Branch `ghc-vis`. +-- Weitere Informationen in der README.md --- Ein paar Infos --- -------------- - --- In der Vorlesung haben Sie schon kennengelernt, dass Haskell 'Lazy' ist. In --- dieser Übung werden Sie dies graphisch erfahren können. - --- Hinweis: --- Da wir direkt auf den Evaluationstatus von einzelnen Ausdrücken zugreifen --- wollen, und sich der compilierte ByteCode von unterschiedlichen GHC Versionen --- sehr unterschiedlich ist, arbeiten wir für diese Übung mit GHC < 8.* - - --- Zur Visualisierung der Ausdrücke nutzen wir ghc-vis. Dies integriert sich --- dank eines Scripts direkt in den GHCi. - --- Befehle: - --- :vis Öffnet das ghc-vis Fenster (haben Sie dies geschlossen müssen Sie --- vermutlich den GHCi nocheinmal starten bevor Sie ghc-vis erneut öffnen --- können - --- :view x Zeigt den Ausdruck "x" grafisch an - --- :switch Schaltet den Anzeigemodus um - --- :clear Säubert die Anzeige - --- Aufgabenstellung --- ---------------- - -ones = [1,1..] - -list = [1,3..] - -list' = [1,5..] - --- Starten Sie nun ghc-vis mit dem Befehl ":vis" und lassen sich zunächst `ones` --- anzeigen ":view ones" --- Sie können einfach auf einen Eintrag klicken um diesen evaluieren zu lassen --- (dafür darf der Eintrag noch nicht vollständig evaluiert sein). - --- Dies können Sie nun auch mit den anderen Listen machen. - --- (1) Erstellen Sie eine PDF (ghc-vis > File > Export) in der die Listen bis --- mindestens zu ihrem 10ten Element ausgewertet sind. Fassen Sie zusätzlich in --- Stichpunkten die Unterschiede der Listen zusammen. - - - --- Lassen Sie sich nun auf die gleiche Art eine unendliche Liste aller Fibonacci --- Zahlen anzeigen. - -f = 0:1:zipWith (+) f (tail f) - --- :clear --- :view f - --- (2) Interpretieren Sie das nun sichtbare in kurzen Stichpunkten. Erstellen Sie nun --- eine PDF in der die ersten 10 Fibonacci Zahlen UNAUSGEWERTET (also als Thunks) --- vorliegen. Nun sollte es für Sie möglich sein, mit einem Klick alle noch nicht --- berechneten Fibonacci Zahlen berechnen zu lassen. Speichern Sie auch diesen --- Graph als PDF. - - -primes = sieve [2..] where sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p > 0] - --- Lassen sie sich `primes` im ghc-vis anzeigen. Dies stellt eine unendliche Liste --- von Primzahlen dar, die durch das "Sieb des Eratosthenes" erzeugt werden. --- Das Prinzip dieses Siebes ist es, Primzahlen zu finden, indem jede Zahl mit --- 2 beginnend `[2..]` durch alle bereits gefundenden Primzahlen geteilt wird, um --- zu prüfen, ob eine unteilbare Zahl, und damit eine Primzahl, vorliegt. - --- (3) Erstellen Sie eine PDF, in der noch nicht gefilterte/überprüfte Zahlen --- (5-10) zu erkennen sind, sowie bereits als Primzahl erkannte Zahlen UND Zahlen, die --- bereits teilweise gefiltert sind. - - -result = "No need to write code for this exercise!" - +result = "foo" diff --git a/src/Aufgabe4.lhs b/src/Aufgabe4.lhs index 7185466..cbf40b0 100644 --- a/src/Aufgabe4.lhs +++ b/src/Aufgabe4.lhs @@ -1,85 +1,6 @@ -Aufgabe 4 -========= - > module Aufgabe4 where +Aufgabe 4 finden befindet sich auf dem Branch `ghc-vis`. +Weitere Informationen in der README.md -Ein paar Infos --------------- - -In der Vorlesung haben Sie schon kennengelernt, dass Haskell 'Lazy' ist. In -dieser Übung werden Sie dies graphisch erfahren können. - -Hinweis: -Da wir direkt auf den Evaluationstatus von einzelnen Ausdrücken zugreifen -wollen, und sich der compilierte ByteCode von unterschiedlichen GHC Versionen -sehr unterschiedlich ist, arbeiten wir für diese Übung mit GHC < 8.* - - -Zur Visualisierung der Ausdrücke nutzen wir ghc-vis. Dies integriert sich -dank eines Scripts direkt in den GHCi. - -Befehle: - -:vis Öffnet das ghc-vis Fenster (haben Sie dies geschlossen müssen Sie - vermutlich den GHCi nocheinmal starten bevor Sie ghc-vis erneut öffnen - können - -:view x Zeigt den Ausdruck "x" grafisch an - -:switch Schaltet den Anzeigemodus um - -:clear Säubert die Anzeige - -Aufgabenstellung ----------------- - -> ones = [1,1..] -> -> list = [1,3..] -> -> list' = [1,5..] - -Starten Sie nun ghc-vis mit dem Befehl ":vis" und lassen sich zunächst `ones` -anzeigen ":view ones" -Sie können einfach auf einen Eintrag klicken um diesen evaluieren zu lassen -(dafür darf der Eintrag noch nicht vollständig evaluiert sein). - -Dies können Sie nun auch mit den anderen Listen machen. - -(1) Erstellen Sie eine PDF (ghc-vis > File > Export) in der die Listen bis -mindestens zu ihrem 10ten Element ausgewertet sind. Fassen Sie zusätzlich in -Stichpunkten die Unterschiede der Listen zusammen. - - - -Lassen Sie sich nun auf die gleiche Art eine unendliche Liste aller Fibonacci -Zahlen anzeigen. - -> f = 0:1:zipWith (+) f (tail f) - -:clear -:view f - -(2) Interpretieren Sie das nun sichtbare in kurzen Stichpunkten. Erstellen Sie nun -eine PDF in der die ersten 10 Fibonacci Zahlen UNAUSGEWERTET (also als Thunks) -vorliegen. Nun sollte es für Sie möglich sein, mit einem Klick alle noch nicht -berechneten Fibonacci Zahlen berechnen zu lassen. Speichern Sie auch diesen -Graph als PDF. - - -> primes = sieve [2..] where sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p > 0] - -Lassen sie sich `primes` im ghc-vis anzeigen. Dies stellt eine unendliche Liste -von Primzahlen dar, die durch das "Sieb des Eratosthenes" erzeugt werden. -Das Prinzip dieses Siebes ist es, Primzahlen zu finden, indem jede Zahl mit -2 beginnend `[2..]` durch alle bereits gefundenden Primzahlen geteilt wird, um -zu prüfen, ob eine unteilbare Zahl, und damit eine Primzahl, vorliegt. - -(3) Erstellen Sie eine PDF, in der noch nicht gefilterte/überprüfte Zahlen -(5-10) zu erkennen sind, sowie bereits als Primzahl erkannte Zahlen UND Zahlen, die -bereits teilweise gefiltert sind. - - -> result = "No need to write code for this exercise!" - +> result = "foo" diff --git a/src/Aufgabe4.md b/src/Aufgabe4.md index 7185466..cbf40b0 100644 --- a/src/Aufgabe4.md +++ b/src/Aufgabe4.md @@ -1,85 +1,6 @@ -Aufgabe 4 -========= - > module Aufgabe4 where +Aufgabe 4 finden befindet sich auf dem Branch `ghc-vis`. +Weitere Informationen in der README.md -Ein paar Infos --------------- - -In der Vorlesung haben Sie schon kennengelernt, dass Haskell 'Lazy' ist. In -dieser Übung werden Sie dies graphisch erfahren können. - -Hinweis: -Da wir direkt auf den Evaluationstatus von einzelnen Ausdrücken zugreifen -wollen, und sich der compilierte ByteCode von unterschiedlichen GHC Versionen -sehr unterschiedlich ist, arbeiten wir für diese Übung mit GHC < 8.* - - -Zur Visualisierung der Ausdrücke nutzen wir ghc-vis. Dies integriert sich -dank eines Scripts direkt in den GHCi. - -Befehle: - -:vis Öffnet das ghc-vis Fenster (haben Sie dies geschlossen müssen Sie - vermutlich den GHCi nocheinmal starten bevor Sie ghc-vis erneut öffnen - können - -:view x Zeigt den Ausdruck "x" grafisch an - -:switch Schaltet den Anzeigemodus um - -:clear Säubert die Anzeige - -Aufgabenstellung ----------------- - -> ones = [1,1..] -> -> list = [1,3..] -> -> list' = [1,5..] - -Starten Sie nun ghc-vis mit dem Befehl ":vis" und lassen sich zunächst `ones` -anzeigen ":view ones" -Sie können einfach auf einen Eintrag klicken um diesen evaluieren zu lassen -(dafür darf der Eintrag noch nicht vollständig evaluiert sein). - -Dies können Sie nun auch mit den anderen Listen machen. - -(1) Erstellen Sie eine PDF (ghc-vis > File > Export) in der die Listen bis -mindestens zu ihrem 10ten Element ausgewertet sind. Fassen Sie zusätzlich in -Stichpunkten die Unterschiede der Listen zusammen. - - - -Lassen Sie sich nun auf die gleiche Art eine unendliche Liste aller Fibonacci -Zahlen anzeigen. - -> f = 0:1:zipWith (+) f (tail f) - -:clear -:view f - -(2) Interpretieren Sie das nun sichtbare in kurzen Stichpunkten. Erstellen Sie nun -eine PDF in der die ersten 10 Fibonacci Zahlen UNAUSGEWERTET (also als Thunks) -vorliegen. Nun sollte es für Sie möglich sein, mit einem Klick alle noch nicht -berechneten Fibonacci Zahlen berechnen zu lassen. Speichern Sie auch diesen -Graph als PDF. - - -> primes = sieve [2..] where sieve (p:xs) = p : sieve [x | x <- xs, x `mod` p > 0] - -Lassen sie sich `primes` im ghc-vis anzeigen. Dies stellt eine unendliche Liste -von Primzahlen dar, die durch das "Sieb des Eratosthenes" erzeugt werden. -Das Prinzip dieses Siebes ist es, Primzahlen zu finden, indem jede Zahl mit -2 beginnend `[2..]` durch alle bereits gefundenden Primzahlen geteilt wird, um -zu prüfen, ob eine unteilbare Zahl, und damit eine Primzahl, vorliegt. - -(3) Erstellen Sie eine PDF, in der noch nicht gefilterte/überprüfte Zahlen -(5-10) zu erkennen sind, sowie bereits als Primzahl erkannte Zahlen UND Zahlen, die -bereits teilweise gefiltert sind. - - -> result = "No need to write code for this exercise!" - +> result = "foo" diff --git a/src/DataPB.hs b/src/DataPB.hs new file mode 100644 index 0000000..3ea8e5d --- /dev/null +++ b/src/DataPB.hs @@ -0,0 +1,9 @@ +module DataPB where + +import qualified Data.Attoparsec.Text as A +import Data.Attoparsec.Combinator +import qualified Data.Text as T + +simpleData :: [(String,String)] +simpleData = [("Phillip","(04165) 6048168"),("Michael","(0148) 56121127"),("Phillip","(03548) 4538973"),("Paula","(030) 664940"),("Paul","(088) 08933337"),("Sylvester","(035588) 582174"),("Harriet","(0751) 75825269"),("Ariane","(0302) 49046118"),("Michael","(06650) 6975458"),("Kylie","(046) 77199945"),("Lee","(032509) 210171"),("Hunter","(06929) 7068643"),("Marvin","(036623) 741589"),("Paul","(0229) 13909736"),("Linda","(0859) 15505790"),("Lydia","(039229) 379675"),("Michael","(0755) 98407141"),("Skyler","(035869) 897089"),("Ariane","(035) 22332444"),("Eden","(0080) 20592805"),("John","(09857) 4504229"),("Jenette","(0182) 34362051"),("Delilah","(035690) 139671"),("Michael","(034973) 388559"),("John","(0451) 08133630"),("Madonna","(03897) 0007997"),("Madeline","(0600) 19801141"),("Francesca","(087) 58002539"),("Latifah","(04010) 3533294"),("Lucas","(08963) 4253453"),("Acton","(034060) 453837"),("Shelby","(039459) 871551"),("Keiko","(047) 07336335"),("Moses","(017) 73610349"),("Samantha","(034114) 317044"),("Logan","(00017) 2404967"),("Marcia","(041) 04583123"),("Allistair","(0707) 04978234"),("Hedwig","(037227) 674138"),("Phillip","(01175) 6633514"),("Paul","(084) 47394790"),("Paula","(0243) 06440875"),("Michael","(065) 98178780"),("Paul","(00622) 0087233"),("Stacy","(004) 38785128"),("Stephen","(043) 84694380"),("Michael","(053) 55705629"),("Christine","(001) 44243108"),("Paul","(030513) 239204"),("Phillip","(07043) 5061835"),("Paula","(036638) 3341652")] + diff --git a/src/FunPB.hs b/src/FunPB.hs new file mode 100644 index 0000000..09d1eb3 --- /dev/null +++ b/src/FunPB.hs @@ -0,0 +1,21 @@ +module FunPB where + +import Control.Monad + +data FunPB a b = FunPB { runFunPB :: a -> (a,[b]) } + +instance Monoid (FunPB a b) where + mempty = FunPB $ \k -> (k,mempty) + mappend pb1 pb2 = FunPB $ \n -> (,) n $ msum . (<$>) (uncurry (flip const).((flip runFunPB) n)) $ [pb1,pb2] + +addAssoc :: Eq a => (a,b) -> FunPB a b -> FunPB a b +addAssoc (n,nr) pb = FunPB $ \n' -> if n' == n then ((:) nr) <$> runFunPB pb n' else runFunPB pb n' + +delAssoc :: Eq a => a -> FunPB a b -> FunPB a b +delAssoc n pb = FunPB $ \n' -> if n == n' then runFunPB mempty n' else runFunPB pb n' + +multiFind :: [FunPB a b] -> a -> (a,[b]) +multiFind = runFunPB.mconcat + +dataToFunPB :: Eq a => [(a,b)] -> FunPB a b +dataToFunPB = mconcat.fmap ((flip addAssoc) mempty) diff --git a/stack.yaml b/stack.yaml index 3ff8d70..8c4a91d 100644 --- a/stack.yaml +++ b/stack.yaml @@ -1,11 +1,5 @@ -flags: {} extra-package-dbs: [] packages: - '.' -extra-deps: -- ghc-vis-0.8 -- svgcairo-0.13.1.1 -- xdot-0.3.0.1 -- Cabal-1.24.2.0 -- gtk2hs-buildtools-0.13.2.2 -resolver: lts-6.31 +extra-deps: [] +resolver: lts-8.9 diff --git a/test/Aufgabe1-Spec.hs b/test/Aufgabe1-Spec.hs new file mode 100644 index 0000000..2f66216 --- /dev/null +++ b/test/Aufgabe1-Spec.hs @@ -0,0 +1,66 @@ +import Aufgabe1 + +import Test.Framework.Providers.HUnit (testCase) +import Test.Framework.Runners.Console (defaultMain) +import Test.HUnit + + +{- TEST DATA -} + +data1,data2,data3 :: [(String,String)] +data1 = [("Phillip","(04165) 9876543"),("Phillip","(03548) 1234567"),("Paula","(035383) 567890")] +data2 = [("Paula","(04165) 8765432"),("Michael","(03548) 2345678"),("Ariane","(035383) 678901")] +data3 = [("Paula","(04165) 7654321"),("Hannelore","(03548) 3456789"),("Helmut","(035383) 789012")] + +noData :: [(String,String)] +noData = mempty + +xPB :: PhoneBook +xPB = PB $ \n -> [b|(a,b)<-data1, n==a] + +yPB :: PhoneBook +yPB = PB $ \n -> [b|(a,b)<-data2, n==a] + +zPB :: PhoneBook +zPB = PB $ \n -> [b|(a,b)<-data3, n==a] + +examplePB = xPB + +emptyPhoneBook :: PhoneBook +emptyPhoneBook = PB $ \a -> [b|(a,b)<-noData] + +{- TEST CASES -} + +usePBTest = testCase "Benutze beispielhaftes PhoneBook" + $ assertEqual "usePB examplePB \"Phillip\" sollte folgende zwei Nummern rausgeben" ["(04165) 9876543","(03548) 1234567"] + $ usePB examplePB "Phillip" + +mappendTest = testCase "Assoziativität von mappend" + $ assertEqual "Assoziativität von mappend ist nicht erfüllt" (usePB (mappend (mappend xPB yPB) zPB) "Paula") + $ usePB (mappend xPB $ mappend yPB zPB) "Paula" + +memptyTest1 = testCase "Linksidentität von mempty" + $ assertEqual "Linksidentität von mempty ist nicht erfüllt" (usePB xPB "Phillip") + $ usePB (mappend xPB mempty) "Phillip" + +memptyTest2 = testCase "Rechtsidentität von mempty" + $ assertEqual "Rechtsidentität von mempty ist nicht erfüllt" (usePB xPB "Phillip") + $ usePB (mappend mempty xPB) "Phillip" + +addEntryTest = testCase "Füge Name-Nummer-Verknüpfung hinzu" + $ assertEqual "Die hinzugefügte Nummer wird nicht gefunden" ["12345"] + $ usePB (addEntry "NeuerName" "12345" emptyPhoneBook) "NeuerName" + +delEntryTest = testCase "Lösche Name-Nummer-Verknüpfung" + $ assertEqual "Die gelöschte Nummer wird weiterhin gefunden" [] + $ usePB (delEntry "Paula" xPB) "Paula" + +findInMultTest = testCase "Suche in mehreren PhoneBooks" + $ assertEqual "Es werden nicht alle drei Nummern von Paula gefunden" ["(035383) 567890","(04165) 8765432","(04165) 7654321"] + $ findInMult [xPB,yPB,zPB] "Paula" + + +tests = [usePBTest,mappendTest,memptyTest1,memptyTest2,addEntryTest,delEntryTest,findInMultTest] + +main :: IO () +main = defaultMain tests diff --git a/test/Aufgabe2-Spec.hs b/test/Aufgabe2-Spec.hs new file mode 100644 index 0000000..12a1ad9 --- /dev/null +++ b/test/Aufgabe2-Spec.hs @@ -0,0 +1,7 @@ +main :: IO () +main = putStrLn $ "Eine Testung von Aufgabe 2 mit HUnit ist nicht sinnvoll. \n" + ++ "Falls Sie feststecken und nicht weiterkommen: \n" + ++ "Welche Typklassen kennen Sie? \n" + ++ "Wie sehen deren Instanzen für die vorliegenden Datentypen aus? \n" + ++ "Welche Regeln/Gesetze implizieren diese Typklassen? \n" + ++ "Eventuell lassen sich Ausdrücke mehrfach verallgemeinern. \n" \ No newline at end of file diff --git a/test/Aufgabe3-Spec.hs b/test/Aufgabe3-Spec.hs new file mode 100644 index 0000000..389e555 --- /dev/null +++ b/test/Aufgabe3-Spec.hs @@ -0,0 +1,77 @@ +import Aufgabe3 +import DataPB +import FunPB +import AreaCode + +import Test.Framework.Providers.HUnit (testCase) +import Test.Framework.Runners.Console (defaultMain) +import Test.HUnit + + +{- TEST DATA -} + +funPBTest = dataToFunPB simpleData + +vielleichtValues = [(Etwas 1234),Nichts] + +entwederValues :: [Entweder String [[[Integer]]]] +entwederValues = [(Jenes "just a string"),(Dieses [[[12344321]]])] + +konstantValue = Konstant True + +f = const 1 +g = const '0' + + +{- TEST CASES-} + +atleast5CharFalse = testCase "Teste Randbedingung (==False) für atleast5Char" + $ assertEqual "Der Ausdruck >>> runPred atLeast5Char $ \"1234\" \nsollte zu False auswerten" False + $ runPred atLeast5Char $ "1234" + +atleast5CharTrue = testCase "Teste Randbedingung (==True) für atleast5Char" + $ assertEqual "Der Ausdruck >>> runPred atLeast5Char $ \"12345\" \nsollte zu True auswerten" True + $ runPred atLeast5Char $ "12345" + + + +fmapVielleichtTestIdentity = testCase "Functor Vielleicht: Strukturerhaltung" + $ assertEqual "Es sollte gelten: fmap id == id . " (id <$> vielleichtValues) + $ ((fmap id) <$> vielleichtValues) + +fmapVielleichtTestComposability = testCase "Functor Vielleicht: Komponierbarkeit" + $ assertEqual "Es sollte gelten: fmap f . fmap g == fmap (f . g) . " ((fmap f . fmap g) <$> vielleichtValues) + $ ((fmap (f . g)) <$> vielleichtValues) + + +fmapEntwederTestIdentity = testCase "Functor (Entweder a): Strukturerhaltung" + $ assertEqual "Es sollte gelten: fmap id == id . " (id <$> entwederValues) + $ ((fmap id) <$> entwederValues) + +fmapEntwederTestComposability = testCase "Functor (Entweder a): Komponierbarkeit" + $ assertEqual "Es sollte gelten: fmap f . fmap g == fmap (f . g) . " ((fmap f . fmap g) <$> entwederValues) + $ ((fmap (f . g)) <$> entwederValues) + + +fmapKonstantTestIdentity = testCase "Functor (Konstant a): Strukturerhaltung" + $ assertEqual "Es sollte gelten: fmap id == id . " (id konstantValue) + $ ((fmap id) konstantValue) + +fmapKonstantTestComposability = testCase "Functor (Konstant a): Komponierbarkeit" + $ assertEqual "Es sollte gelten: fmap f . fmap g == fmap (f . g) . " ((fmap f . fmap g) konstantValue) + $ ((fmap (f . g)) konstantValue) + + + +fmapFunPBTestIdentity = testCase "Functor (FunPB a): Strukturerhaltung" + $ assertEqual "Es sollte gelten: fmap id == id . " (runFunPB (id funPBTest) "Paula") + $ (runFunPB ((fmap id) funPBTest) "Paula") + +fmapFunPBTestComposability = testCase "Functor (FunPB a): Komponierbarkeit" + $ assertEqual "Es sollte gelten: fmap f . fmap g == fmap (f . g) . " (runFunPB (fmap snd . fmap separateAreaCode $ funPBTest) "Paula") + $ (runFunPB ((fmap (snd.separateAreaCode)) funPBTest) "Paula") + +tests = [atleast5CharFalse,atleast5CharTrue,fmapVielleichtTestIdentity,fmapVielleichtTestComposability,fmapEntwederTestIdentity,fmapEntwederTestComposability,fmapKonstantTestIdentity,fmapKonstantTestComposability,fmapFunPBTestIdentity,fmapFunPBTestComposability] + +main :: IO () +main = defaultMain tests \ No newline at end of file diff --git a/zettel2.cabal b/zettel2.cabal index fe38512..07b7d96 100644 --- a/zettel2.cabal +++ b/zettel2.cabal @@ -18,8 +18,74 @@ cabal-version: >=1.10 -- to solve assignments library hs-source-dirs: src - exposed-modules: Aufgabe4 + exposed-modules: Lib + , DataPB + , AreaCode + , FunPB + , Aufgabe1 + , Aufgabe2 + , Aufgabe3 + , Aufgabe4 build-depends: base >= 4.7 && < 5 + , attoparsec + , text + default-language: Haskell2010 + +executable aufgabe1 + hs-source-dirs: app + main-is: Aufgabe1Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: base + , zettel2 + default-language: Haskell2010 + +test-suite aufgabe1-tests + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Aufgabe1-Spec.hs + build-depends: base + , zettel2 + , test-framework + , test-framework-hunit + , HUnit + ghc-options: -threaded -rtsopts -with-rtsopts=-N + default-language: Haskell2010 + +executable aufgabe2 + hs-source-dirs: app + main-is: Aufgabe2Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: base + , zettel2 + default-language: Haskell2010 + +test-suite aufgabe2-tests + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Aufgabe2-Spec.hs + build-depends: base + , zettel2 + ghc-options: -threaded -rtsopts -with-rtsopts=-N + default-language: Haskell2010 + +executable aufgabe3 + hs-source-dirs: app + main-is: Aufgabe3Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: base + , zettel2 + default-language: Haskell2010 + +test-suite aufgabe3-tests + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Aufgabe3-Spec.hs + build-depends: base + , zettel2 + , test-framework + , test-framework-hunit + , HUnit + ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010 executable aufgabe4 @@ -28,7 +94,15 @@ executable aufgabe4 ghc-options: -threaded -rtsopts -with-rtsopts=-N build-depends: base , zettel2 - , ghc-vis + default-language: Haskell2010 + +test-suite aufgabe4-tests + type: exitcode-stdio-1.0 + hs-source-dirs: test + main-is: Aufgabe4-Spec.hs + build-depends: base + , zettel2 + ghc-options: -threaded -rtsopts -with-rtsopts=-N default-language: Haskell2010 source-repository head