first commit, zettel2 without solutions
This commit is contained in:
commit
363316dc2e
19
.gitignore
vendored
Normal file
19
.gitignore
vendored
Normal file
@ -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
|
41
.travis.yml
Normal file
41
.travis.yml
Normal file
@ -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
|
||||||
|
|
30
LICENSE
Normal file
30
LICENSE
Normal file
@ -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.
|
6
app/Aufgabe1Main.hs
Normal file
6
app/Aufgabe1Main.hs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
import Aufgabe1
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn result
|
6
app/Aufgabe2Main.hs
Normal file
6
app/Aufgabe2Main.hs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
import Aufgabe2
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn result
|
6
app/Aufgabe3Main.hs
Normal file
6
app/Aufgabe3Main.hs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
import Aufgabe3
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn result
|
6
app/Aufgabe4Main.hs
Normal file
6
app/Aufgabe4Main.hs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Main where
|
||||||
|
|
||||||
|
import Aufgabe4
|
||||||
|
|
||||||
|
main :: IO ()
|
||||||
|
main = putStrLn result
|
27
src/AreaCode.hs
Normal file
27
src/AreaCode.hs
Normal file
@ -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
|
||||||
|
|
82
src/Aufgabe1.hs
Normal file
82
src/Aufgabe1.hs
Normal file
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
72
src/Aufgabe1.lhs
Normal file
72
src/Aufgabe1.lhs
Normal file
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
79
src/Aufgabe1.md
Normal file
79
src/Aufgabe1.md
Normal file
@ -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)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
41
src/Aufgabe2.hs
Normal file
41
src/Aufgabe2.hs
Normal file
@ -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?"
|
||||||
|
|
||||||
|
|
||||||
|
|
45
src/Aufgabe2.lhs
Normal file
45
src/Aufgabe2.lhs
Normal file
@ -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?"
|
||||||
|
|
||||||
|
|
||||||
|
|
47
src/Aufgabe2.md
Normal file
47
src/Aufgabe2.md
Normal file
@ -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?"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
147
src/Aufgabe3.hs
Normal file
147
src/Aufgabe3.hs
Normal file
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
168
src/Aufgabe3.lhs
Normal file
168
src/Aufgabe3.lhs
Normal file
@ -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")
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
171
src/Aufgabe3.md
Normal file
171
src/Aufgabe3.md
Normal file
@ -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")
|
||||||
|
|
||||||
|
```
|
||||||
|
|
7
src/Aufgabe4.hs
Normal file
7
src/Aufgabe4.hs
Normal file
@ -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"
|
7
src/Aufgabe4.lhs
Normal file
7
src/Aufgabe4.lhs
Normal file
@ -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"
|
7
src/Aufgabe4.md
Normal file
7
src/Aufgabe4.md
Normal file
@ -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"
|
9
src/DataPB.hs
Normal file
9
src/DataPB.hs
Normal file
@ -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")]
|
||||||
|
|
21
src/FunPB.hs
Normal file
21
src/FunPB.hs
Normal file
@ -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)
|
6
src/Lib.hs
Normal file
6
src/Lib.hs
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
module Lib
|
||||||
|
( someFunc
|
||||||
|
) where
|
||||||
|
|
||||||
|
someFunc :: IO ()
|
||||||
|
someFunc = putStrLn "someFunc"
|
5
stack.yaml
Normal file
5
stack.yaml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
extra-package-dbs: []
|
||||||
|
packages:
|
||||||
|
- '.'
|
||||||
|
extra-deps: []
|
||||||
|
resolver: lts-8.9
|
66
test/Aufgabe1-Spec.hs
Normal file
66
test/Aufgabe1-Spec.hs
Normal file
@ -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
|
7
test/Aufgabe2-Spec.hs
Normal file
7
test/Aufgabe2-Spec.hs
Normal file
@ -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"
|
77
test/Aufgabe3-Spec.hs
Normal file
77
test/Aufgabe3-Spec.hs
Normal file
@ -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
|
5
test/Aufgabe4-Spec.hs
Normal file
5
test/Aufgabe4-Spec.hs
Normal file
@ -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. "
|
110
zettel2.cabal
Normal file
110
zettel2.cabal
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user