1.3) Funktionen
In diesem Casino wird mit Chips gespielt, die an der Kasse gekauft werden, wo selbstverständlich Transaktionsgebühren anfallen. Alle Chips haben den gleichen Wert. Im ersten Schritt setzen wir die Grundrechenarten als Funktionen mit Integer-Werten um. Es kann davon ausgegangen werden, dass die Integer-Werte nie negativ sind.
- Eine konstante Funktion
fee
soll die Transaktionsgebühr festsetzen. Sie soll vorerst einen Chip betragen. - Die Funktion
charge val
zieht die Gebühr von einem gegebenen Wert ab. Das Ergebnis kann aber nicht negativ werden, d.h. von null Chips wird auch nichts abgezogen - Die Funktion
putChips owned added
soll zwei Chip-Anzahlen zusammenrechnen. Sie funktioniert wie die Addition, auf das Ergebnis wird aber die Transaktionsgebühr fällig. - Die Funktion
takeChips owned taken
zieht einen Chipstapel taken von owned ab, z.B. wenn man einen Einsatz macht. Da man aber nicht mehr einsetzen kann als man hat, kann das Ergebnis der Operation nicht kleiner als 0 werden - entsprechend einem All-In.
-- Constant that defines the transaction fee
fee :: Int
fee = 1
-- Reduce the number of chips passed on by the transaction fee
charge :: Int -> Int
charge a = if a > fee then a - fee else 0
-- Increase the number of chips passed on by the amount passed on. A fee is due.
putChips :: Int -> Int -> Int
putChips owned bought = charge (owned + bought)
-- Reduce the number of chips passed on by the amount passed on.
takeChips :: Int -> Int -> Int
takeChips owned taken =
let result = owned - taken in
if result >= 0 then result else 0
-- fee = 1 -- Transaktionsgebühr beträgt 1 Euro
-- charge 10 = 9 -- Kauft man 10 Chips, erhält man nur 9
-- putChips 10 2 = 11 -- Kaufen wir 2 neue Chips und haben wir danach 11 Chips
-- takeChips 11 5 = 7 -- Spielen wir 5 unserer 11 Chips, bleiben noch 7
1.4) Rekursion
Euer Casino-Rechner beherrscht nun einige Grundfunktionen, im Casino gibt es aber noch Spezial-Gewinne. Die Funktion win :: Int -> Int -> Int
ist ein spezieller Gewinn-Modus für die Spielautomaten, bei dem zwei Chipstapel multipliziert werden. Es gibt aber in den AGBs eine gestaffelte Gebühr, mit der auch verhindert werden soll, dass man einen zu großen Gewinn mit einem zu kleinen Einsatz holt. Sei a der größere der beiden Operanden a und b. Dann gibt es a Iterationen, in denen b auf das Ergebnis addiert wird (Multiplikation durch Addition). Jedes Mal wenn die Anzahl der übrigen Iterationen durch 10 teilbar ist, wird b für die nachfolgenden Iterationen um 1 verringert (bis b=0 erreicht ist).
win :: Int -> Int -> Int
win a b = if a > b then recurse a b else recurse b a
where
recurse :: Int -> Int -> Int
recurse a b = if a > 0 && b > 0 then
if a `mod` 10 == 0 then
b + recurse (a-1) (b-1)
else
b + recurse (a-1) b
else 0
2.1 Rekursion II
Variante mit pattern matching
win2 :: Int -> Int -> Int
win2 a b = if a > b then recurse a b else recurse b a
where
recurse a 0 = 0
recurse 0 b = 0
recurse a b =
let b' = if a `mod` 10 == 0 then b-1 else b
in b + recurse (a-1) b'
-- win2 20 0 = 0
-- win2 20 5 = 72
-- win2 5 20 = 72
-- win2 20 2 = 12
-- win2 100000000 10000000 = *** Exception: stack overflow
Variante mit tail recursion
win3 :: Int -> Int -> Int
win3 a b = if a > b then recurse a b 0 else recurse b a 0
where
recurse _ 0 p = p
recurse 0 _ p = p
recurse a b p =
let b' = if a `mod` 10 == 0 then b-1 else b
in recurse (a - 1) b' $! (b + p)
-- win3 20 0 = 0
-- win3 20 5 = 72
-- win3 5 20 = 72
-- win3 20 2 = 12
-- win3 100000000 10000000 = 499999960000000
2.1* Fibonaccily
Langsame Variante mit top-down
fib :: Int -> Int
fib 0 = 0
fib 1 = 1
fib a = fib (a - 1) + fib (a - 2)
-- fib 7 = 13
-- fib 30 = 832040 -- langsam
-- fib 70 = 190392490709135 -- braucht ewig
Schnelle Variante mit tail call optimization (bottom-up)
fib2 :: Int -> Int
fib2 a = let n = if a < 0 then 0 else a in fibHelper 0 1 n
fibHelper :: Int -> Int -> Int -> Int
fibHelper curr prev 0 = curr
fibHelper curr prev n = fibHelper (curr + prev) curr $! n - 1
-- fib2 7 = 13
-- fib2 30 = 832040
-- fib2 70 = 190392490709135 -- immer noch extrem schnell
2.2 Eigene Datentypen
data CommandS = Put | Take | Win deriving Show -- Summentyp/Aufzaehlungstyp
evalS :: CommandS -> Int -> Int -> Int
evalS c a b = case c of -- alternative zum pattern matching
Put -> putChips a b
Take -> takeChips a b
Win -> win a b
-- | 2b) Commands with parameters
data CommandP = PutP Int Int | TakeP Int Int | WinP Int Int deriving Show
evalP :: CommandP -> Int
evalP (PutP a b) = putChips a b
evalP (TakeP a b) = takeChips a b
evalP (WinP a b) = win a b
-- | 3c) Oh the recursiveness!
data CommandR = PutR CommandR CommandR
| TakeR CommandR CommandR
| WinR CommandR CommandR
| ValR Int
deriving Show
evalR :: CommandR -> Int
evalR (PutR c1 c2) = putChips (evalR c1) (evalR c2)
evalR (TakeR c1 c2) = takeChips (evalR c1) (evalR c2)
evalR (WinR c1 c2) = win (evalR c1) (evalR c2)
evalR (ValR v) = v
-- Blatt 1
fee = 1 :: Int
charge :: Int -> Int
charge a = if a > fee then a - fee else 0
putChips, takeChips, win :: Int -> Int -> Int
putChips owned bought = charge (owned + bought)
takeChips owned taken = let result = owned - taken in
if result >= 0 then result else 0
win a b = if a > b then recurse a b 0 else recurse b a 0
where
recurse _ 0 p = p
recurse 0 _ p = p
recurse a b p =
let b' = if a `mod` 10 == 0 then b-1 else b
in recurse (a - 1) b' $! (b + p)
2.3 Arbeiten mit Strings
ascii = ['\0'..'\127'] -- Liste aller ASCII Zeichen
isVowel ('a') = True
isVowel ('e') = True
isVowel ('i') = True
isVowel ('o') = True
isVowel ('u') = True
isVowel _ = False
-- isVowel 'a' = True
-- isVowel '\0' = False
hasVowel [] = False
hasVowel ('a':xs) = True
hasVowel ('e':xs) = True
hasVowel ('i':xs) = True
hasVowel ('o':xs) = True
hasVowel ('u':xs) = True
hasVowel (_:xs) = hasVowel xs
-- hasVowel "cake" = True
-- hasVowel "rhythm" = False
toString :: CommandR -> String
toString command = case command of
ValR x -> show x
PutR x y -> "(" ++ toString x ++ " + " ++ toString y ++ ")"
TakeR x y -> "(" ++ toString x ++ " - " ++ toString y ++ ")"
WinR x y -> "(" ++ toString x ++ " * " ++ toString y ++ ")"
data CommandR = PutR CommandR CommandR
| TakeR CommandR CommandR
| WinR CommandR CommandR
| ValR Int
deriving Show
-- toString (WinR (ValR 4) (PutR (WinR (ValR 2) (ValR 3)) (ValR 5))) = "(4 * ((2 * 3) + 5))"
3.1 Parametrisierte Datentypen
rep <list> <num_repetitions>
⇒ <list>
rep :: [Int] -> Int -> [Int]
rep x 0 = []
rep x n = x `mappend` rep x (n-1)
l :: [Int]
l = [1,2,3]
-- rep l 3 = [1,2,3,1,2,3,1,2,3]
-- rep l 0 = []
-- rep [] 2 = []
mirror <list>
⇒ <list>
mirror :: [Int] -> [Int]
mirror x = x `mappend` reverse x
l :: [Int]
l = [1,2,3]
-- mirror l = [1,2,3,3,2,1]
-- mirror [] = []
drop2 <list>
⇒ <list>
drop2 :: [Int] -> [Int]
drop2 [] = []
drop2 (_:[]) = []
drop2 (_:_:xs) = xs
l :: [Int]
l = [1,2,3,4]
-- drop2 l = [3,4]
-- drop 2 [1,2] = []
-- drop2 [1] = []
-- drop2 [] = []
3.3 Listenfunktionale I
nvcls :: String -> String
nvcls = filter (not.is_vocal)
is_vocal :: Char -> Bool
is_vocal c = c `elem` "aeiouAEIOU"
t = "Today is the first day of the month."
-- nvcls t = "Tdy s th frst dy f th mnth."
sqr, pot :: Int -> Int
sqr = (^ 2)
pot = (2 ^)
-- map sqr [0..10] = [0,1,4,9,16,25,36,49,64,81,100]
-- map pot [0..10] = [1,2,4,8,16,32,64,128,256,512,1024]
data CommandR = PutR CommandR CommandR | TakeR CommandR CommandR | WinR CommandR CommandR | ValR Int deriving Show
-- am besten listen wir erstmal die Blattwerte von einzelnen CommandR
values (ValR v) = [v]
values (PutR a b) = (values a) ++ (values b)
values (TakeR a b) = (values a) ++ (values b)
values (WinR a b) = (values a) ++ (values b)
-- Dann können wir für eine Liste alle zusammenfügen
listValues = (foldr (++) []) . (map values)
-- listValues [ValR 0, TakeR (ValR 5) (ValR 10)] = [0,5,10]
4.1 Listenfunktionale II
data CommandR = PutR CommandR CommandR | TakeR CommandR CommandR | WinR CommandR CommandR | ValR Int deriving Show
badluck :: CommandR -> [CommandR] -> CommandR
badluck start bets = foldl TakeR start bets
badluckr :: CommandR -> [CommandR] -> CommandR
badluckr start bets = foldr (flip TakeR) start bets
-- badluck (ValR 10) [ValR 4, ValR 2] = TakeR (TakeR (ValR 10) (ValR 4)) (ValR 2)
-- badluckr (ValR 10) [ValR 4, ValR 2] = TakeR (TakeR (ValR 10) (ValR 2)) (ValR 4)
import Prelude hiding (any)
any :: (a -> Bool) -> [a] -> Bool
any pred [] = False
any pred (x:xs) = if pred x then True else any pred xs
-- | Funktioniert genauso wie any!
any' pred = foldr (||) False . map pred
-- any (>= 5) [1..4] = False
-- any (>= 5) [1..10] = True
-- any (>= 5) [1..] = True -- funktioniert mit unendlichen Listen!
-- any (< 5) [5…] = …… -- aber nur wenn das Ergebnis True ist
import Prelude hiding (elem)
elem y = any (==y)
-- elem 'a' "Hallo" = True
hasVowel :: String -> Bool
hasVowel = any isVowel
isVowel = (`elem` "aeiou") :: Char -> Bool
-- hasVowel "rhythm" = False
-- hasVowel "cake" = True
4.2 List Comprehensions
-- ersten 20 Vielfachen von 7
a = [i * 7 | i <- [2..21]]
-- jede Zahl bis 100, die nicht durch fünf teilbar ist
b = [i | i <- [1..100],
mod i 5 /= 0]
-- Alle Primzalhen bis 100
c = [i | i <- [2..100],
let l2 = [j | j <- [2..(div i 2)],
(mod i j) == 0],
length l2 == 0]
-- Alle Dreierkombinationen von Vornamen für den Perso
d = [ name | i <- einzelnamen,
j <- einzelnamen,
i /= j, -- "Jon-Jon-Bran" wollen wir nicht
k <- einzelnamen,
j /= k, -- "Theon-Jon-Jon" wollen wir nicht
i /= k, -- "Jon-Samwell-Jon" wollen wir nicht
let name = i ++ "-" ++ j ++ "-" ++ k ++ " Schubert",
length name < 27 -- muss auf dem Perso passen
]
where einzelnamen = ["Jon", "Theon", "Samwell", "Joffrey", "Bran"]
4.3 Typklassen
-- Aus Blatt 1
fee = 1 :: Int
charge :: Int -> Int
charge a = if a > fee then a - fee else 0
putChips, takeChips, win :: Int -> Int -> Int
putChips owned bought = charge (owned + bought)
takeChips owned taken = let result = owned - taken in
if result >= 0 then result else 0
win a b = if a > b then recurse a b 0 else recurse b a 0
where
recurse _ 0 p = p
recurse 0 _ p = p
recurse a b p =
let b' = if a `mod` 10 == 0 then b-1 else b
in recurse (a - 1) b' $! (b + p)
data Command = Put Command Command | Take Command Command | Win Command Command | Val Int | Sig Command
eval :: Command -> Int
eval (Put c1 c2) = putChips (eval c1) (eval c2)
eval (Take c1 c2) = takeChips (eval c1) (eval c2)
eval (Val v) = v
eval (Win c1 c2) = win (eval c1) (eval c2)
eval (Sig v) = signum (eval v)
--
toString :: Command -> String
toString command = case command of
Val x -> show x
Sig x -> "signum " ++ toString x
Put x y -> "(" ++ toString x ++ " + " ++ toString y ++ ")"
Take x y -> "(" ++ toString x ++ " - " ++ toString y ++ ")"
Win x y -> "(" ++ toString x ++ " * " ++ toString y ++ ")"
instance Show Command where
show = toString
instance Num Command where
c1 + c2 = Put c1 c2
c1 - c2 = Take c1 c2
c1 * c2 = Win c1 c2
abs c1 = c1
signum c1 = Sig c1
fromInteger x
| x >= 0 = Val (fromInteger x)
instance Eq Command where
c1 == c2 = eval c1 == eval c2
instance Ord Command where
c1 <= c2 = eval c1 <= eval c2
-- show (Put (Win (Val 2) (Val 3)) (Val 5)) = "((2 * 3) + 5)"
-- abs (2 * 3) + 5 :: Command = ((2 * 3) + 5)
-- 2 * 3 + 5 == (17 - 6 :: Command) = False
-- 2 * 3 + 5 == Val 10 = True
-- 2 * 3 + 5 < Val 9 = False
-- 2 * 3 + 5 > Val 9 = True
-- :t max = max :: Ord a => a -> a -> a
-- max (2 * 3 + 5) (Val 9) = ((2 * 3) + 5)
Sources:
Related:
Tags: Softwaretechnik und Programmierparadigmen