summaryrefslogtreecommitdiff
path: root/ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2015-12-03 06:21:07 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2015-12-03 06:21:07 +0000
commitb8c490a23087557af0ce8fd0482edff62139c6ab (patch)
tree8ac5b5bd5dbb59e3e8bf37cbd7d41383af778967 /ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs
parent93f5010782c801104efca7c1da875a01d046ab50 (diff)
downloaduni-b8c490a23087557af0ce8fd0482edff62139c6ab.tar
uni-b8c490a23087557af0ce8fd0482edff62139c6ab.tar.gz
uni-b8c490a23087557af0ce8fd0482edff62139c6ab.tar.bz2
uni-b8c490a23087557af0ce8fd0482edff62139c6ab.tar.xz
uni-b8c490a23087557af0ce8fd0482edff62139c6ab.zip
Started on FFP - 07
Diffstat (limited to 'ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs')
-rw-r--r--ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs312
1 files changed, 312 insertions, 0 deletions
diff --git a/ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs b/ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs
new file mode 100644
index 0000000..942fd63
--- /dev/null
+++ b/ws2015/ffp/blaetter/07/FFP_U07_Monaden2.hs
@@ -0,0 +1,312 @@
1{-# LANGUAGE RankNTypes #-} -- I like being allowed to specify type signatures
2
3-- Fortgeschrittene Funktionale Programmierung,
4-- LMU, TCS, Wintersemester 2015/16
5-- Steffen Jost, Alexander Isenko
6--
7-- Übungsblatt 07. 2.12.2015
8--
9-- Thema: Monaden (Teil2)
10--
11-- Anweisung:
12-- Gehen Sie diese Datei durch und bearbeiten Sie
13-- alle Vorkommen von undefined bzw. die mit -- !!! TODO !!!
14-- markierten Stellen. Testen Sie Ihre Lösungen mit GHCi!
15--
16--
17
18import qualified Data.Map as Map
19import Data.Map (Map)
20import Control.Applicative
21import Control.Monad
22
23import Control.Monad.ST
24import Data.STRef
25
26import Data.Maybe (fromMaybe)
27
28import Debug.Trace (traceShowId)
29
30---- A7-1 I/O und "Hello World!"
31
32-- a)
33-- Lassen Sie das Programm von Folie 4-77 laufen!
34-- Erstellen Sie dazu eine separate Datei mit dem
35-- Haskell-Quelltext.
36-- Verwenden Sie GHC und nicht GHCI!
37--
38-- HINWEIS:
39-- GHC erstellt nur eine ausführbare Datei,
40-- falls die Funktion main existiert.
41-- Falls Ihr Modul oder Ihre Funktion anders heisst,
42-- so sollten Sie bei der Kompilation die
43-- Option "-main-is" verwenden, also z.B.
44-- "ghc MyFile.hs -main-is MyModule.myfoo"
45
46
47-- b)
48-- Schreiben Sie ein ausführbares Programm,
49-- welches den Benutzer zur Eingabe einer Textzeile auffordert.
50-- Ihr Programm soll abwechselnd
51-- - die Textzeile rückwärts ausgeben
52-- - die Textzeile ausgeben, wobei
53-- A/Ä durch E, p durch b und k durch g ersetzt werden
54-- Das Programm beendet sich, falls der Benutzer
55-- nichts eingibt und nur return drückt.
56--
57-- Beispielausführung:
58-- Hallo Benutzer!
59-- Sag was, ich sag's Dir dann umgekehrt:
60-- Was ist ein Palindrom?
61-- ?mordnilaP nie tsi saW
62-- Sag emol ebbes lustiges:
63-- Äpfel und Birnen
64-- Ebfel und Birnen
65-- Sag was, ich sag's Dir dann umgekehrt:
66--
67-- Bye bye!
68--
69
70main :: IO ()
71main = do
72 putStrLn "Hallo Benutzer!"
73 main' actions
74 putStrLn "Bye bye!"
75 where
76 actions = cycle [ ("Sag was, ich sag's Dir dann umgekehrt:", reverse)
77 , ("Sag emal ebbes lustiges:", dialekt)
78 ]
79 main' ((str, a):as) = do
80 putStrLn str
81 l <- getLine
82 when (not $ null l) $ do
83 putStrLn $ a l
84 main' as
85
86dialekt :: String -> String
87dialekt = map dialekt'
88 where
89 dialekt' 'A' = 'E'
90 dialekt' 'Ä' = 'E'
91 dialekt' 'p' = 'b'
92 dialekt' 'k' = 'g'
93 dialekt' x = x
94
95
96
97---- A7-2 Allgemeine Monadische Funktionen
98--
99-- Implementieren Sie folgende Funktionen aus
100-- der Standarbibliothek selbst zu Fuss.
101-- Sie dürfen DO-Notation einsetzen, wenn Sie möchten.
102--
103-- Zur Vermeidung von Namenskonflikten
104-- wurde jedem Funktionsnamen das Kürzel "my" vorangestellt.
105
106
107-- a) reguläres foldl mit monadischer Funktion
108--
109myFoldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
110myFoldM _ a [] = return a
111myFoldM f a (b:bs) = do
112 a' <- (a `f` b)
113 myFoldM f a' bs
114
115
116-- b) Replizieren eines monadischen Wertes
117-- Beispiel:
118-- > myReplicateM 3 (return 7)
119-- [7,7,7]
120--
121-- > myReplicateM 4 (putStr "X ")
122-- X X X X [(),(),(),()]
123--
124myReplicateM :: (Monad m) => Int -> m a -> m [a]
125myReplicateM 0 _ = return []
126myReplicateM n a = do
127 a' <- a
128 (a' :) `liftM` myReplicateM (n - 1) a
129
130
131-- c) Hintereinanderausführung zweier monadischer Funktionen
132-- (<=<) :: Monad m => (b -> m c) -> (a -> m b) -> a -> m c
133--
134myR2Lcomposition :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
135myR2Lcomposition f g a = g a >>= f
136
137
138
139-- A7-3 Anwenden der Zustandsmonade
140--
141-- Gegeben ist die korrekte Berechnung einer Wahrscheinlichkeit für einen Kampf in einem Strategiespiel.
142-- Die Berechnung ist uns zu langsam. Da dabei viele wiederholte Berechnungen auftreten,
143-- möchten wir die gesamte Berechnung durch explizite Memoisation mit einer Zustandsmonade beschleunigen!
144--
145-- IHRE AUFGABE:
146-- Berechnung der gegebenen Funktion "winchance" dramatisch beschleunigen!
147--
148-- Das Ergebnis soll natürlich unverändert sein! Deklarieren Sie also zum Vergleich
149-- eine neue Funktion "memoWinchance" mit gleichem Typ.
150--
151-- Dabei haben Sie die Wahl:
152-- - Kreieren Sie eine Zustandsmonade zu Fuss, wie in der letzten Vorlesung demonstriert; oder
153-- - Verwenden Sie die Module Control.Monad.ST und Data.STRef
154-- Für letztere Variante folgt am Ende dieser Datei ein Programmbeispiel zur Verwendung: demoST
155--
156-- Warum die gegebene Wahrscheinlichkeitsberechnung richtig ist, ist für unser Lernziel unwichtig.
157-- Der Hintergrund "Strategiespiel" dient nur der Illustration, damit die Aufgabe nicht zu langweilig wird.
158-- Es ist also nicht schlimm, wenn Sie die Details davon nicht verstehen.
159--
160
161--
162-- BESCHREIBUNG UND MODELLIERUNG DES STRATEGIESPIELS
163--
164-- In einem Strategiespiel kämpfen verschiedene fantastische Monster gegeneinander.
165-- Eine Monster besitzt zwei Werte: Kampfstärke und Trefferpunkte:
166--
167
168data Monster = Monster Int Int deriving (Show,Eq,Ord)
169-- HINWEIS: Hier könnte man Record-Syntax gut einsetzen. Wir verzichten jedoch darauf,
170-- da Record-Syntax noch nicht in der Vorlesung behandelt wurde.
171
172strength :: Monster -> Int
173strength (Monster s _) = s
174
175hitpoints :: Monster -> Int
176hitpoints (Monster _ h) = h
177
178takeHit :: Monster -> Monster
179takeHit (Monster s h) = Monster s $ h-1
180
181-- Einige Standard-Monster zum Testen:
182crow = Monster 5 1
183human = Monster 10 2
184orc = Monster 20 1
185dwarf = Monster 15 2
186elf = Monster 25 2
187giant = Monster 20 4
188knight= Monster 35 2
189
190-- Ein Kampf zwischen zwei Parteien in diesem Spiel läuft wie folgt ab:
191--
192-- Die Monster eines Kampfe zwischen zwei gegnerischen Parteien sind
193-- jeweils in einem Stapel geordnet:
194-- also eine Liste von Angreifern und eine Liste von Verteidigern.
195-- Es kämpfen immer nur jeweils die beiden Monster an vorderster Front der beiden Stapel gegeneinander.
196--
197-- Beide Monster würfeln eine Zahl zwischen 0 und 99 und vergleichen diese mit Ihrer eigenen Stärke.
198-- Ist die gewürfelte Zahl kleiner als die Stärke, so verliert das gegnerische Monster einen Trefferpunkt;
199-- beide Würfelwürfe werden jedoch wiederholt, falls beide Monster Ihren Stärketest gleichzeitig bestehen oder beide durchfallen,
200-- es verliert also immer nur genau eines der beiden obersten Monster einen Trefferpunkt.
201--
202-- Sobald ein Monster 0 Trefferpunkte hat, wird es aus dem Stapel entfernt.
203-- Die verbleibenden Monster kämpfen, bis einer der beiden gegnerischen Stapel leer ist.
204--
205-- Um uns im Spiel einen Vorteil zu verschaffen, möchten wir die Gewinnwahrscheinlichkeit
206-- für zwei gegebene Stacks von Monstern im Voraus ausrechnen.
207-- Dies haben wir auch schnell in Haskell implementiert:
208--
209-- Die Funktion winchance berechnet uns die Gewinnwahrscheinlichkeit
210-- für den ersten Stapel aus (d.h. mindestens ein Angreifermonster überlebt).
211
212dice = 100
213
214winchance :: [Monster] -> [Monster] -> Double
215winchance [] _ = 0
216winchance _ [] = 1
217winchance allatt@(a:att) alldef@(d:def)
218 | hitpoints a <= 0 = winchance att alldef -- remove dead attacker
219 | hitpoints d <= 0 = winchance allatt def -- remove dead defender
220 | otherwise =
221 let attchance = nextHitchance dice (strength a) (strength d) -- chance for next hit by attacker on defender
222 defchance = 1 - attchance -- chance for next hit by defender on attacker
223 atthit = winchance allatt (takeHit d:def) -- attacker's winchance if defender took a hit
224 defhit = winchance (takeHit a:att) alldef -- attacker's winchance if attacker took a hut
225 in (attchance * atthit) + (defchance * defhit) -- overall winchance for first stack
226
227-- Die Funktion nextHitchance 100 x y rechnet uns die Chance aus,
228-- das ein Monster mit Stärke x gegen ein Monster mit Stärke y den nächsten Treffer landet.
229-- Möglicherweise auftretende Wiederholungen der Würfelwürfe haben wir
230-- mit Hilfe der Mathematik über die Grenzwerte unendlicher Reihen bereits vereinfacht:
231
232nextHitchance :: Int -> Int -> Int -> Double
233nextHitchance die attstr defstr = a*d' / (d*a' + a*d')
234 where
235 a = (fromIntegral attstr)/(fromIntegral die)
236 a' = if a > 1 then 0 else 1 - a
237 d = (fromIntegral defstr)/(fromIntegral die)
238 d' = if d > 1 then 0 else 1 - d
239
240{-
241Der Code funktioniert auch prinzipiell:
242
243> nextHitchance 100 10 20
2440.30769230769230776
245
246> winchance [orc,dwarf,elf,giant] [human,human,human,knight,knight]
2470.35168883031403275
248
249> winchance [human,human,human,knight,knight] [orc,dwarf,elf,giant]
2500.6483111696859674
251
252> winchance [orc,orc,orc,dwarf,dwarf,giant,giant] [human,human,human,elf,elf,knight,knight]
2530.33760541326192256
254
255jedoch zeigt das letzte Beispiel, dass die Berechnung sehr lange dauern kann!
256
257Dabei wird jedoch oft das Gleiche frisch berechnet:
258Zum Beispiel bei der Berechnung von "winchance [orc,elf] [human,dwarf]"
259wird zwei mal "winchance [elf] [dwarf]" frisch ausgerechnet.
260Einmal unter der Annahme, dass zuerst der orc stirbt, und
261dann nochmal unter der Annahme das zuerst der mensch besiegt wurde.
262
263Es bietet sich daher an, die Werte von winchance in einer Tabelle abzulegen,
264z.B. Map ([Monster],[Monster]) Double
265Ein Aufruf von memoWinchance schaut zuerst in dieser Tabelle nach,
266ob die Wahrscheinlichkeit für diesen kompletten (Rest-)Kampf bereits bekannt ist.
267Falls ja, wird schnell der bekannte Wert zurückgegeben.
268Falls nein, wird der Wert berechnet und anschliessend in die Tabelle eingetragen.
269Beachten Sie, das bei der Berechnung eines Wertes die Tabelle bereits wachsen kann!
270
271Hinweis: Da die Tabelle für verschieden Stacks schnell groß werden kann,
272verzichten wir darauf, diese zwischen verschiedenen Aufrufen von winchance
273im Speicher zu halten. Nur während der Rekursion in einer Hilfsfunktion
274von winchance führen wir die Tabelle immer mit.
275-}
276
277type MM = Map ([Monster], [Monster]) Double -- MM stands for MonsterManual
278
279memoWinchance :: [Monster] -> [Monster] -> Double
280memoWinchance atts defs = runST (newSTRef Map.empty >>= memoWinchance' atts defs)
281
282memoWinchance' :: forall s. [Monster] -> [Monster] -> STRef s MM -> ST s Double
283memoWinchance' [] _ = 0
284memoWinchance' _ [] = 1
285memoWinchance' atts@(a:att) defs@(d:def) mmRef = (Map.lookup (atts, defs) <$> readSTRef mmRef) >>= fromMaybe winchance . fmap return . traceShowId
286 where
287 memoize x = modifySTRef mmRef (Map.insert (atts, defs) x)
288 winchance
289 | hitpoints a <= 0 =
290
291--
292-- Simples Beispiel zur Verwendung der ST-Monade:
293--
294demoST :: (Integer, [Char])
295demoST = runST $ do
296 somerefA <- newSTRef 0
297 somerefB <- newSTRef "A"
298 increment somerefA
299 increment somerefA
300 modifySTRef somerefB $ \s -> s ++ "B"
301 increment somerefA
302 a <- readSTRef somerefA
303 b <- readSTRef somerefB
304 increment somerefA
305 increment somerefA
306 return (a,b)
307 where
308 increment ref = do
309 x <- readSTRef ref
310 let y = succ x
311 writeSTRef ref y
312