commit 363316dc2ef74c13abf29d6ea8fd60ebc1288847 Author: BergesJ Date: Sun Apr 30 20:51:45 2017 +0200 first commit, zettel2 without solutions diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a4ee41a --- /dev/null +++ b/.gitignore @@ -0,0 +1,19 @@ +dist +dist-* +cabal-dev +*.o +*.hi +*.chi +*.chs.h +*.dyn_o +*.dyn_hi +.hpc +.hsenv +.cabal-sandbox/ +cabal.sandbox.config +*.prof +*.aux +*.hp +*.eventlog +.stack-work/ +cabal.project.local diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..5ccfeba --- /dev/null +++ b/.travis.yml @@ -0,0 +1,41 @@ +# This is the simple Travis configuration, which is intended for use +# on applications which do not require cross-platform and +# multiple-GHC-version support. For more information and other +# options, see: +# +# https://docs.haskellstack.org/en/stable/travis_ci/ +# +# Copy these contents into the root directory of your Github project in a file +# named .travis.yml + +# Use new container infrastructure to enable caching +sudo: false + +# Do not choose a language; we provide our own build tools. +language: generic + +# Caching so the next build will be fast too. +cache: + directories: + - $HOME/.stack + +# Ensure necessary system libraries are present +addons: + apt: + packages: + - libgmp-dev + +before_install: +# Download and unpack the stack executable +- mkdir -p ~/.local/bin +- export PATH=$HOME/.local/bin:$PATH +- travis_retry curl -L https://www.stackage.org/stack/linux-x86_64 | tar xz --wildcards --strip-components=1 -C ~/.local/bin '*/stack' + +install: +# Build dependencies +- stack --no-terminal --install-ghc test --only-dependencies + +script: +# Build the package, its tests, and its docs and run the tests +- stack --no-terminal test --haddock --no-haddock-deps + diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6a042c2 --- /dev/null +++ b/LICENSE @@ -0,0 +1,30 @@ +Copyright Author name here (c) 2017 + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Author name here nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/Setup.hs b/Setup.hs new file mode 100644 index 0000000..9a994af --- /dev/null +++ b/Setup.hs @@ -0,0 +1,2 @@ +import Distribution.Simple +main = defaultMain 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/app/Aufgabe4Main.hs b/app/Aufgabe4Main.hs new file mode 100644 index 0000000..637355c --- /dev/null +++ b/app/Aufgabe4Main.hs @@ -0,0 +1,6 @@ +module Main where + +import Aufgabe4 + +main :: IO () +main = putStrLn result 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 new file mode 100644 index 0000000..e6f009e --- /dev/null +++ b/src/Aufgabe4.hs @@ -0,0 +1,7 @@ +module Aufgabe4 where + +-- Aufgabe 4 finden Sie auf dem branch `ghc-vis`. +-- Sie können auf diesen mit dem Befehl `git checkout ghc-vis` gelangen. +-- Führen Sie dort zunächst das Installationsscript `install_dependencies.sh` aus. + +result = "foo" diff --git a/src/Aufgabe4.lhs b/src/Aufgabe4.lhs new file mode 100644 index 0000000..b40a457 --- /dev/null +++ b/src/Aufgabe4.lhs @@ -0,0 +1,7 @@ +> module Aufgabe4 where + +Aufgabe 4 finden Sie auf dem branch `ghc-vis`. +Sie können auf diesen mit dem Befehl `git checkout ghc-vis` gelangen. +Führen Sie dort zunächst das Installationsscript `install_dependencies.sh` aus. + +> result = "foo" diff --git a/src/Aufgabe4.md b/src/Aufgabe4.md new file mode 100644 index 0000000..b40a457 --- /dev/null +++ b/src/Aufgabe4.md @@ -0,0 +1,7 @@ +> module Aufgabe4 where + +Aufgabe 4 finden Sie auf dem branch `ghc-vis`. +Sie können auf diesen mit dem Befehl `git checkout ghc-vis` gelangen. +Führen Sie dort zunächst das Installationsscript `install_dependencies.sh` aus. + +> 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/src/Lib.hs b/src/Lib.hs new file mode 100644 index 0000000..d36ff27 --- /dev/null +++ b/src/Lib.hs @@ -0,0 +1,6 @@ +module Lib + ( someFunc + ) where + +someFunc :: IO () +someFunc = putStrLn "someFunc" diff --git a/stack.yaml b/stack.yaml new file mode 100644 index 0000000..8c4a91d --- /dev/null +++ b/stack.yaml @@ -0,0 +1,5 @@ +extra-package-dbs: [] +packages: +- '.' +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/test/Aufgabe4-Spec.hs b/test/Aufgabe4-Spec.hs new file mode 100644 index 0000000..153b901 --- /dev/null +++ b/test/Aufgabe4-Spec.hs @@ -0,0 +1,5 @@ +main :: IO () +main = putStrLn $ "Für Aufgabe 4 sind keine Tests vorgesehen. \n" + ++ "Zur Bearbeitung von Aufgabe 4 wechseln Sie bitte auf den branch `ghc-vis`. \n" + ++ "Dies erreichen Sie mit dem Befehl `git checkout ghc-vis`. \n" + ++ "Führen Sie dort zunächst das Installationsscript `install_dependencies.sh` aus. " diff --git a/zettel2.cabal b/zettel2.cabal new file mode 100644 index 0000000..07b7d96 --- /dev/null +++ b/zettel2.cabal @@ -0,0 +1,110 @@ +name: zettel2 +version: 0.1.0.0 +synopsis: Second Assignment of FFPiHaskell 2017 +-- description: +homepage: https://github.com/FFPiHaskell/zettel2-skeleton#readme +license: BSD3 +license-file: LICENSE +author: FFPiHaskell Tutors +maintainer: sdressel@techfak.uni-bielefeld.de +copyright: 2017 FFPiHaskell Tutors +category: cli +build-type: Simple +extra-source-files: README.md +cabal-version: >=1.10 + + +-- library for all things common in all exercises/not neccessary for students +-- to solve assignments +library + hs-source-dirs: src + 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 + hs-source-dirs: app + main-is: Aufgabe4Main.hs + ghc-options: -threaded -rtsopts -with-rtsopts=-N + build-depends: base + , zettel2 + 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 + type: git + location: https://github.com/FFPiHaskell/zettel2-skeleton