summaryrefslogtreecommitdiff
path: root/ws2015
diff options
context:
space:
mode:
authorGregor Kleen <gkleen@yggdrasil.li>2015-11-11 19:45:19 +0000
committerGregor Kleen <gkleen@yggdrasil.li>2015-11-11 19:45:19 +0000
commitffef46d79685a189fc4e733e865a8aa1c51a00d5 (patch)
treeb54c3f8f8209499aa340d824521fbd9412295686 /ws2015
parent44eb92e9f3adbc3f06e363dacb00f7fd0a37f6b3 (diff)
parent3089deff57382181fe76ff45e14d44c299f72b2e (diff)
downloaduni-ffef46d79685a189fc4e733e865a8aa1c51a00d5.tar
uni-ffef46d79685a189fc4e733e865a8aa1c51a00d5.tar.gz
uni-ffef46d79685a189fc4e733e865a8aa1c51a00d5.tar.bz2
uni-ffef46d79685a189fc4e733e865a8aa1c51a00d5.tar.xz
uni-ffef46d79685a189fc4e733e865a8aa1c51a00d5.zip
Merge branch 'master' of git.yggdrasil.li:gkleen/pub/uni
Diffstat (limited to 'ws2015')
-rw-r--r--ws2015/FFP/blaetter/04/FFP_U04_Lazy.hs380
1 files changed, 380 insertions, 0 deletions
diff --git a/ws2015/FFP/blaetter/04/FFP_U04_Lazy.hs b/ws2015/FFP/blaetter/04/FFP_U04_Lazy.hs
new file mode 100644
index 0000000..f82b54c
--- /dev/null
+++ b/ws2015/FFP/blaetter/04/FFP_U04_Lazy.hs
@@ -0,0 +1,380 @@
1-- Fortgeschrittene Funktionale Programmierung,
2-- LMU, TCS, Wintersemester 2015/16
3-- Steffen Jost, Alexander Isenko
4--
5-- Übungsblatt 04. 11.11.2015
6--
7-- Thema:
8--
9-- Anweisung:
10-- Gehen Sie diese Datei durch und bearbeiten Sie
11-- alle Vorkommen von undefined bzw. die mit -- !!! TODO !!!
12-- markierten Stellen. Testen Sie Ihre Lösungen mit GHCi!
13--
14--
15
16import Data.Map as Map
17import Data.Set as Set
18
19
20
21---- A4-1 Verzögerte Auswertung
22-- Gegeben ist folgendes Programm:
23xs = [1..]
24foo x = 2 * x
25ys = foo <$> xs
26rs = take 1 $ drop 1 ys
27
28{-Skizzieren Sie mit Papier und Bleistift,
29 wie am Ende der Speicher aussieht, wenn lediglich
30 rs
31 ausgewertet wurde (z.B. durch Anzeige am Bildschirm).
32
33 Welche Strukturen gibt es im Speicher?
34 Wie viele Thunks befinden sich noch im Speicher?
35 Auf welche Adressen zeigen xs, ys, rs in den von Ihnen skizzierten Speicher?
36
37 Hinweise:
38 - An jeder Speicheraddresse sollte sich entweder ein Thunk (also ein Programmausdruck)
39 oder ein Wert befinden.
40 - Für Datentypen wird der Wert dargestellt als Tupel aus Konstruktor
41 und den Speicheraddressen seiner Argumente.
42 - Funktionsdefinitonen lassen wir zur Vereinfachung aus dem Speicher heraus.
43 - Speicheraddressen dürfen Sie völlig willkürlich wählen
44
45 BEISPIEL:
46
47 -- Programm:
48 zs = [27..]
49 ts = take 1 $ drop 2 zs
50 x = head ts
51
52 -- Nach Auswertung von x haben wir den Zustand:
53
54 [ <01>,(:),<11>,<02> | <02>,(:),<12>,<03> | <03>,(:),<13>,<04> | <04>,"[30..]"
55 | <11>,Int,27 | <12>,Int,27+1 | <13>,Int,29
56 | <21>,(:),<13>,<22> | <22>,[]
57 ]
58
59 Thunks: <04>,<12>
60
61 zs -> <01>
62 ts -> <21>
63 x -> <13>
64 -}
65
66
67
68
69---- A4-2 Zirkularität
70-- a)
71-- Schreiben Sie ein zirkuläres Programm transcl,
72-- welches zu einer gegebenen Relation r :: a -> [a]
73-- und einer als Liste gegebenen Menge,
74-- die transitive Hülle dieser Menge zu der Relation berechnet.
75--
76-- Eine Relation r :: a -> [a] ist dabei so kodiert,
77-- das r x die Menge aller Elemente ist, welche zu x in Relation stehen.
78--
79-- HINWEIS:
80-- Das Ergebnis soll eine Menge modellieren, es darf also kein Element
81-- doppelt vorkommen. Die Reigenfolge der Elemente in der Liste ist aber egal.
82--
83-- BEISPIELE:
84--
85-- > transCl rel1 [22]
86-- [33,44]
87-- > transCl rel1 [2,5]
88-- [2,5,4,6,8,1,3,7,9]
89--
90-- > sort $ transCl rel2 [42,8,9]
91-- [1,2,4,5,7,8,9,10,11,13,14,16,17,20,21,22,26,28,32,34,40,42,52,64]
92--
93-- HINWEIS: Folgen Sie dem nub2 Beispiel aus Folie 3-30
94
95
96transCl :: (Eq a) => (a -> [a]) -> [a] -> [a]
97transCl r xs = undefined -- !!! TODO !!!
98
99-- Zum Testen:
100rel1 11 = [22]
101rel1 22 = [33]
102rel1 33 = [44]
103rel1 n
104 | even n, n>=1, n<=9 = [2,4,6,8]
105 | odd n, n>=1, n<=9 = [1,3,5,7,9]
106 | otherwise = [n]
107
108rel2 n
109 | even n = [n,n `div` 2]
110 | otherwise = [3*n+1,n]
111
112
113-- b)
114-- Implementieren Sie die Aufgabe noch mal ganz schnell
115-- ohne Rücksicht auf Zirkularität oder Effizienz,
116-- sondern ganz bequem mit der Standardbibliothek für Data.Set
117
118transClS :: (Ord a) => (a -> Set a) -> Set a -> Set a
119transClS rel set = undefined -- !!! TODO !!!
120
121
122
123
124---- A4-3 Verzögerte Auswertung
125{-Ein Kollege von Dr Jost meinte einmal, dass man Dinge am Besten durch Implementation erlernt.
126 Also wollen wir in dieser Aufgabe verzögerte Auswertung für einen Fragment von Haskell Implementieren.
127 Wir machen uns das Leben einfach und nehemen nur folgendes, minimales Fragment her:
128 * Variablen z.B. "y"
129 * Anonyme Funktionen z.B. "\x->x"
130 * Funktionsanwendung z.B. "(\x->x) y" evaluiert zu "y"
131 Wir verzichten also auf sehr viel: keine Pattern-Matching, kein if-then-else,...
132 ...sogar auf Basisdatentypen wie Int oder Bool verzichten wir!
133
134 Die Terme unseres Sprach-Fragments modellieren wir durch folgenden Datentyp:
135 -}
136
137data Term = Var Variable | Abs Variable Term | App Term Term
138 deriving (Eq)
139
140type Variable = String
141
142{-
143 Einige Hilfsfunktionen, sowie einige Beispiel-Terme sind weiter unten,
144 im Anhang dieser Datei definiert. Ebenso wurde eine vernünftige Show-Instanz vorgegeben.
145 so dass die Terme wie Haskell-Code ausschauen:
146 *Main> Abs "x" (App (Var "f") (Var "x" ))
147 \x -> f x
148
149 Von den Hilfsfunktionen sollten Sie eigentlich nur
150 subst :: (Variable, Term) -> Term -> Term
151 benötigen. Der Ausdruck "subst (x,t1) t2" bedeutet "t2[t1/x]",
152 also im Ausdruck t2 werden alle Vorkommen von x durch t1 ersetzt.
153
154
155 NEBENBEMERKUNG;
156 zum Lösen dieser Aufgabe nicht wichtig:
157
158 Richtig, es handelt sich dabei erneut um den Lambda-Kalkül,
159 wie wir ihn schon in Aufgaben A1-1g und A2-2 kennengelernt haben.
160 Anstatt "\x->x" würde man im Lambda-Kalkül eher "λx.x" schreiben, aber
161 das ist auch der einzige Unterschied. O
162 bwohl wir hier auf so viel verzichten, handelt es sich übrigens immer noch um eine Turing-Vollständige Sprache!
163 Also wollen wir hier die verzögerte Auswertestrategie anhand des Lambda-Kalküls üben... ;)
164-}
165
166
167-- a) Einfache Auswertung
168--
169-- Wir schreiben uns zuerst eine simple Auswertung von Lambda-Ausdrücken, also
170-- eine Funktion "eval :: Term -> Term". Das vorgehen ist wie folgt:
171--
172-- 1) Variablen werten zu sich selbst aus (d.h. sind bereits ausgewertet); nichts zu tun.
173--
174-- 2) Abstraktionen sind auch bereits ausgewertet; nichts zu tun.
175-- (Wenn man will, dann könnte man auch erst noch den Rumpf auswerten, soweit möglich.
176-- Dies ist eine reine Definitionsfrage, darf jeder machen wie er will.
177-- Im Allgemeinen wird nicht unter einem Lambda reuduziert, aber beim Testen
178-- werden die Terme leichter verständlich, wenn man unter dem Lambda reduziert.)
179--
180-- 3) Zum Auswerten einer Applikationen "App" muss man zuerst die Funktion auswerten.
181-- Erhält man einen Lambda-Ausdruck "Abs", so ersetzt man alle Vorkommen
182-- der abstrahierten Variable durch das zuvor ausgewertete Funktionsargument.
183-- (Ansonsten liefert man einfach die gesamte Applikation zurück.)
184--
185-- Hinweis: Im 3. Fall muss man noch aufpassen, das keine freien Variablen eingefangen werden.
186-- Glücklicherweise ist dies für uns bereits implementiert. Verwenden Sie einfach die Funktion "subst"
187-- zur Substitution des Funktionsparameters im Rumpf der Funktion.
188-- (Die Funktion "subst" ist weiter unten im Anhang dieser Datei definiert.)
189--
190-- Einfache Implementierung der Auswertung :
191eval :: Term -> Term
192eval = undefined -- !!! TODO !!!
193
194
195{- Beispiele, ohne Auswertung unter einem Lambda, Konstanten cK, c1, usw. sind weiter unten, im Anhang der Datei definiert.
196
197*Main> eval $ App (App cK c1) vX
198\f -> \a -> f a
199
200*Main> eval $ App cISNULL (App cSUCC c0)
201\x -> \y -> y
202
203-}
204
205
206-- b)
207--
208-- Da Haskell eine Sprache mit verzögerter Auswertung ist,
209-- vererbt sich dies bereits auch auf unsere Implementierung von eval:
210-- *Main> eval $ App (App cK c1) cOmega
211-- \f -> \x -> f x
212--
213-- Bei der Auswertestrategie "Call-By-Value" sollte dies eigentlich gar nicht auswerten,
214-- da cOmega nicht endlich auswertbar ist.
215--
216-- Eine Möglichkeit ist es, die Auswertung genauer zu simulieren.
217-- Dazu nutzen wir jetzt eine Map zur Modellierung unseres Speichers:
218
219type Memory = Map Variable Term
220
221-- Ein Wert des Typs "Memory" bildet also Werte des Typs "Variable" auf Werte des Typs "Term" ab.
222--
223-- Zur Anzeige eines Terms benötigen wir nun natürlich auch den Speicher, um den Kontext der Variablen zu haben.
224-- Verwenden Sie zur Anzeige also diese gegebene Funktion:
225showMem :: (Memory,Term) -> Term
226showMem (m,t) = Map.foldlWithKey (\r v s -> subst (v,s) r) t m
227
228-- Diese Anzeige bauchen wir gleich in unsere Auswertefunktion ein:
229evalStrict :: Term -> Term
230evalStrict t = showMem $ evalS0 t
231
232-- Die Funktion evalS0 ist nur eine Kurzform, um die Auswertung mit leerem Speicher zu starten:
233evalS0 ::Term -> (Memory, Term)
234evalS0 = evalS Map.empty
235
236-- Ihre Aufgabe ist es also, evalS zu implementieren:
237
238evalS :: Memory -> Term -> (Memory, Term)
239evalS = undefined -- !!! TODO !!!
240
241-- Dabei verfolgen wir folgende Auswertestrategie:
242--
243-- 1) Der Wert einer Variablen wird im Speicher nachgeschlagen.
244-- Freie Variablen werten nach wie vor zu sich selbst aus.
245--
246-- 2) Unverändert: Abstraktionen sind bereits ausgewertet.
247--
248-- 3) Bei der Auswertung von Applikationen verzichten wir auf Substitution.
249-- Stattdessen legen wir das ausgewertete Argument im Speicher
250-- unter der abstrahierten Variable ab.
251--
252-- Im Fall 3) treten einige Probleme auf:
253-- - Der Speicher muss durch die rekursiven Aufruf von evalS hindurchgefädelt werden.
254-- Es sollte immer nur auf den neuesten Speicher zugegriffen werden,
255-- damit die Modellierung stimmt!
256--
257-- - OPTIONAL: Wenn es aber nur einen Speicher gibt, kann es zu Namenskonflikten im Speicher
258-- kommen. Dies kann vermieden werden, wenn Variable vor dem Speichern in frische Variablen
259-- umbenannt werden. Verwenden Sie dazu die Hilfsfunktionen: freeVars, generateFreshVar & subst.
260--
261-- Wenn Sie es richtig gemacht haben, sollte das obige Beispiel mit cOmega jetzt nicht mehr terminieren.
262
263
264-- c) Nun wollen wir es wieder verbessern, in dem wir verzögerte Auswertung explizit simulieren.
265--
266-- Im Fall 1) updaten wir den Speicher nach der Auswertung des abgespeicherten Terms,
267-- um wiederholte Auswertung zu vermeiden.
268-- (Der Einfachheit verzichten wir auf eine Prüfung/Flag ob im Speicher ein Thunk ist oder nicht:
269-- wir werten und updaten immer - wenn es schon ein Wert war, dann geht die Auswertung ja schnell)
270--
271-- Fall 2) Abs bleibt Unverändert
272--
273-- Im Fall 3) App legen wir also nur den unausgewerten Term im Speicher ab.
274--
275
276evalLazy :: Term -> Term
277evalLazy t = showMem $ evalL0 t
278
279evalL0 ::Term -> (Memory, Term)
280evalL0 = evalL Map.empty
281
282evalL :: Memory -> Term -> (Memory, Term)
283evalL = undefined -- !!! TODO !!!
284
285
286
287-----------------------------------------------------------------------
288-- ANHANG
289--
290-- Für erweiterten Komfort gegeben wir noch einige Definitionen vor!
291--
292
293
294-- Hier einige Lambda-Terme zum Testen:
295vX = Var "x"
296vY = Var "y"
297vZ = Var "z"
298lTest1 = App (App vX vY) vZ
299lTest2 = App vX (App vY vZ)
300
301-- Combinators (allgmein bekannte Lambda-Terme)
302cI = Abs "x" $ Var "x" -- \x -> x
303cK = Abs "x" $ Abs "y" $ Var "x" -- \x y -> x
304cS = Abs "z" $ Abs "y" $ Abs "x" $ App (App (Var "z") (Var "x")) (App (Var "y") (Var "x")) -- \z y x -> (z x) (y x)
305cT = Abs "x" $ App (Var "x") (Var "x") -- \x -> x x
306cOmega = App cT cT -- (\x -> x x) (\x -> x x)
307
308-- Church Booleans (wer keine eingebauten Datentypen hat, bastelt sich halt selbst welche, so wie es uns Alonzo Church zeigte)
309cTRUE = Abs "x" $ Abs "y" $ Var "x" -- \x y -> x
310cFALSE = Abs "x" $ Abs "y" $ Var "y" -- \x y -> y
311cCOND = Abs "x" $ Abs "y" $ Abs "z" $ App (App vX vY) vZ -- \x y z -> (x y) z
312
313-- Church Numerals (wer keine eingebauten Zahlen hat, kann sich auch diese selbst stricken)
314c0 = Abs "f" $ Abs "x" $ Var "x" -- \f x -> x
315c1 = Abs "f" $ Abs "x" $ App (Var "f") (Var "x") -- \f x -> f x
316c2 = eval $ App cSUCC c1 -- \f -> \x -> f (f x)
317c3 = eval $ App cSUCC c2 -- \f -> \x -> f (f (f x))
318cSUCC = Abs "n" $ Abs "f" $ Abs "x" $ App (Var "f") $ App (App (Var "n" ) (Var "f")) (Var "x") -- \n f x -> f ((n f) x)
319cPLUS = Abs "m" $ Abs "n" $ Abs "f" $ Abs "x" $ App (App (Var "m") (Var "f")) $ App (App (Var "n" ) (Var "f")) (Var "x") -- \m n f x -> (m f) ((n f) x)
320cISNULL = Abs "x" $ App (App (Var "x") (Abs "x" cFALSE)) cTRUE -- \x -> x (\x -> (\x y -> y))) (\x y -> x))
321
322-- Lambda Terme hübsch anzeigen, in Haskell Notation, also anstatt "λf.λx.f x" schreiben wir hier "\f-> \x-> f x".
323instance Show Term where
324 showsPrec _ (Var x) = showString x
325 showsPrec n (App s t) = showParen (n>1) $ (showsPrec 1 s) . showString " " . showsPrec 2 t
326 showsPrec n (Abs x t) = showParen (n>0) $ showString ('\\':x) . showString " -> " . showsPrec n t
327
328-----------------------------
329-- Nützliche Hilfsfunktionen
330--
331
332-- Substitution, welche das Einfangen freier Variablen verhindert.
333-- Alle _freien_ Vorkommen einer Variable werden durch einen Term ersetzt.
334-- Gebundene Variablen werden ggf. ersetzt, damit es nicht zu Namenskonflikten kommt:
335-- [y/x] ( \y -> x y ) == \z -> y z
336-- subst ("x", Var "y") (Abs "y" $ App (Var "x") (Var "y"))
337--
338-- Wenn wir die Variable "x" durch den Term "y" ersetzen wollen, dann müssen wir
339-- aufpassen, dass keine gebundenen Variablen 'eingefangen' werden, denn
340-- "\y->x y" ist ja äquivalent zu "\z ->x z".
341-- Also soll auch "(\y->x y)[y/x]" äquivalent zu "(\z ->x z)[y/x]" == "\z->y z" sein.
342-- Wenn wir aber nur blind einsetzen würden, gilt das nicht, denn wir bekämen "\y->y y".
343--
344
345subst :: (Variable, Term) -> Term -> Term
346subst (x,e) o@(Var y)
347 | x == y = e
348 | otherwise = o
349subst s (App e1 e2) = App (subst s e1) (subst s e2)
350subst s@(x,e) o@(Abs y e1)
351 | x == y = o
352 | y `Set.notMember` fv_e = Abs y (subst s e1)
353 | otherwise = Abs freshV (subst (x,e) $ subst (y, Var freshV) e1) -- avoiding capture
354 where
355 fv_e = freeVars e
356 fv_e1 = freeVars e1
357 freshV = generateFreshVar (fv_e `Set.union` fv_e1)
358
359
360-- Freie Variablen eines Terms
361freeVars :: Term -> Set Variable
362freeVars (Var x) = Set.singleton x
363freeVars (App e1 e2) = Set.union (freeVars e1) (freeVars e2)
364freeVars (Abs x e1) = Set.delete x $ freeVars e1
365
366-- Frische Variable berechnen
367generateFreshVar :: Set Variable -> Variable
368generateFreshVar vs
369 | v `Set.notMember` vs = v
370 | otherwise = succString $ Set.findMax vs
371 where
372 v = "a"
373
374-- Note that Ord String has "z" > "aa", so succString s = s ++ "a" would suffice
375-- Ensure that "s" < succString "s"
376succString :: String -> String
377succString "" = "a"
378succString ('z':s) = 'z' : succString s
379succString ( c :s) = (succ c) : s
380