Musterlösungen 9. Übung

Werbung
1 - Testen von Mengen
Zunächst importieren wir die QuickCheck-Bibliothek:
1
import Test.QuickCheck
Für die Ausgabe von QuickCheck benötigen wir zunächst eine Instanz von Show:
2
3
data Set a = Set [a]
deriving Show
Um Mengen raten zu können müssen wir eine Instanz der Typklasse Arbitrary für den Datentyp Set angeben.
Hierbei raten wir einfach eine geeignete Liste:
4
5
6
7
instance Arbitrary a => Arbitrary (Set a) where
arbitrary = do
xs <- arbitrary
return (Set xs)
Wir rekapitulieren noch einmal die angebotenen Funktionen:
8
9
empty :: Set a
empty = Set []
10
11
12
isEmpty :: Set a -> Bool
isEmpty (Set xs) = null xs
13
14
15
insert :: a -> Set a -> Set a
insert x (Set xs) = Set (x:xs)
16
17
18
member :: Eq a => a -> Set a -> Bool
member x (Set xs) = elem x xs
19
20
21
22
23
24
25
delete :: Eq a => a -> Set a ->
delete x (Set xs) = Set (remove
where
remove _ []
=
remove y (z:zs) | y == z
=
| otherwise =
Set a
x xs)
[]
zs
remove y zs
26
27
28
union :: Set a -> Set a -> Set a
union (Set xs) (Set ys) = Set (xs ++ ys)
29
30
31
32
33
34
35
36
37
38
intersect :: Ord a => Set a -> Set a -> Set a
intersect (Set s1) (Set s2) = Set (merge s1 s2)
where
merge []
ys
= ys
merge xs
[]
= xs
merge (x:xs) (y:ys) = case compare x y of
LT -> x : y : merge xs ys
EQ -> y : merge xs ys
GT -> y : x : merge xs ys
39
40
41
size :: Set a -> Int
size (Set xs) = length xs
Nun können wir damit beginnen, Eigenschaften zu definieren.
42
43
prop_isEmpty_empty :: Bool
prop_isEmpty_empty = isEmpty empty
1
44
45
46
prop_member_empty :: Int -> Bool
prop_member_empty x = not (member x empty)
47
48
49
prop_size_empty :: Bool
prop_size_empty = size empty == 0
50
51
52
prop_isEmpty_insert :: Int -> Set Int -> Bool
prop_isEmpty_insert x s = not (isEmpty (insert x s))
53
54
55
prop_member_insert :: Int -> Set Int -> Bool
prop_member_insert x s = member x (insert x s)
56
57
58
prop_member_insert2 :: Int -> Int -> Set Int -> Property
prop_member_insert2 x y s = x /= y ==> member y (insert x s) == member y s
59
60
61
prop_size_insert :: Int -> Set Int -> Bool
prop_size_insert x s = size (insert x s) == size s + (if member x s then 0 else 1)
62
63
64
prop_isEmpty_delete :: Int -> Set Int -> Property
prop_isEmpty_delete x s = isEmpty s ==> isEmpty (delete x s)
65
66
67
prop_member_delete :: Int -> Set Int -> Bool
prop_member_delete x s = not (member x (delete x s))
68
69
70
prop_member_delete2 :: Int -> Int -> Set Int -> Property
prop_member_delete2 x y s = x /= y ==> member y (delete x s) == member y s
71
72
73
prop_size_delete :: Int -> Set Int -> Bool
prop_size_delete x s = size (delete x s) == size s - (if member x s then 1 else 0)
74
75
76
prop_isEmpty_union :: Set Int -> Set Int -> Bool
prop_isEmpty_union s1 s2 = isEmpty (union s1 s2) == isEmpty s1 && isEmpty s2
77
78
79
prop_member_union :: Int -> Set Int -> Set Int -> Bool
prop_member_union x s1 s2 = (member x s1 || member x s2) == member x (union s1 s2)
80
81
82
prop_size_union :: Set Int -> Set Int -> Bool
prop_size_union s1 s2 = size (union s1 s2) == size s1 + size s2 - size (intersect s1 s2)
83
84
85
prop_isEmpty_intersect :: Set Int -> Set Int -> Property
prop_isEmpty_intersect s1 s2 = isEmpty s1 || isEmpty s2 ==> isEmpty (intersect s1 s2)
86
87
88
prop_member_intersect :: Int -> Set Int -> Set Int -> Bool
prop_member_intersect x s1 s2 = (member x s1 && member x s2) == member x (intersect s1 s2)
89
90
91
92
prop_size_intersect :: Set Int -> Set Int -> Bool
prop_size_intersect s1 s2 = s >= 0 && s <= size s1 && s <= size s2
where s = size (intersect s1 s2)
93
94
95
96
97
98
99
100
main :: IO ()
main = do
putStr "prop_isEmpty_empty
: "
quickCheck prop_isEmpty_empty
putStr "prop_member_empty
: "
quickCheck prop_member_empty
putStr "prop_size_empty
: "
2
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
quickCheck prop_size_empty
putStr "prop_isEmpty_insert
: "
quickCheck prop_isEmpty_insert
putStr "prop_member_insert
: "
quickCheck prop_member_insert
putStr "prop_member_insert2
: "
quickCheck prop_member_insert2
putStr "prop_size_insert
: "
quickCheck prop_size_insert
putStr "prop_isEmpty_delete
: "
quickCheck prop_isEmpty_delete
putStr "prop_member_delete
: "
quickCheck prop_member_delete
putStr "prop_member_delete2
: "
quickCheck prop_member_delete2
putStr "prop_size_delete
: "
quickCheck prop_size_delete
putStr "prop_size_delete
: "
quickCheck prop_size_delete
putStr "prop_isEmpty_union
: "
quickCheck prop_isEmpty_union
putStr "prop_member_union
: "
quickCheck prop_member_union
putStr "prop_size_union
: "
quickCheck prop_size_union
putStr "prop_isEmpty_intersect: "
quickCheck prop_isEmpty_intersect
putStr "prop_member_intersect : "
quickCheck prop_member_intersect
putStr "prop_size_intersect
: "
quickCheck prop_size_intersect
Wir sehen, dass nicht alle Tests erfolgreich sind:
Main> main
prop_isEmpty_empty
prop_member_empty
prop_size_empty
prop_isEmpty_insert
prop_member_insert
prop_member_insert2
prop_size_insert
4
Set [1,-3,1,4]
prop_isEmpty_delete
prop_member_delete
5
Set [5,-4,-4,4,5]
prop_member_delete2
-1
0
Set [0,2]
prop_size_delete
-2
Set [1]
prop_size_delete
-2
:
:
:
:
:
:
:
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
Falsifiable, after 3 tests:
: OK, passed 100 tests.
: Falsifiable, after 5 tests:
: Falsifiable, after 1 tests:
: Falsifiable, after 0 tests:
: Falsifiable, after 4 tests:
3
Set [0,2,2]
prop_isEmpty_union
:
Set []
Set [0,2]
prop_member_union
:
prop_size_union
:
Set []
Set [-2]
prop_isEmpty_intersect:
Set []
Set [-1]
prop_member_intersect :
-2
Set []
Set [-2,0,2]
prop_size_intersect
:
Set [1,-2]
Set [-2,-2]
Falsifiable, after 2 tests:
OK, passed 100 tests.
Falsifiable, after 1 tests:
Falsifiable, after 0 tests:
Falsifiable, after 4 tests:
Falsifiable, after 0 tests:
2 - Vorstandsgerangel
1
2
3
4
5
% Auflistung der Kandidaten
kandidat(meier
).
kandidat(mueller ).
kandidat(schulz
).
kandidat(schroeder).
6
7
8
% Verschiedenheit
verschieden(K1, K2) :- kandidat(K1), kandidat(K2), K1 \= K2.
9
10
11
alleVerschieden(vorstand(V,K,S)) :verschieden(V,K), verschieden(V,S), verschieden(K,S).
12
13
14
15
16
% Enthaltensein im Vorstand
imVorstand(P,vorstand(P,_,_)).
imVorstand(P,vorstand(_,P,_)).
imVorstand(P,vorstand(_,_,P)).
17
18
19
nichtImVorstand(P,vorstand(V,K,S)) :verschieden(P,V), verschieden(P,K), verschieden(P,S).
20
21
22
allesKandidaten(vorstand(V,K,S)) :kandidat(V), kandidat(K), kandidat(S).
23
24
% Bedingungen
25
26
27
28
29
% Entweder Meier oder Mueller
meierOderMueller(V) :- imVorstand(meier ,V)
, nichtImVorstand(mueller,V).
meierOderMueller(V) :- imVorstand(mueller,V)
, nichtImVorstand(meier ,V).
meierOderMueller(V) :- nichtImVorstand(mueller,V), nichtImVorstand(meier ,V).
30
31
32
33
34
% Mueller nur wenn Schulz Vorsitzender
muellerNurWennSchulz(vorstand(schulz,K,S)) :imVorstand(mueller,vorstand(schulz,K,S)).
muellerNurWennSchulz(V) :-
4
35
nichtImVorstand(mueller,V) .
36
37
38
39
% Schroeder nur wenn Meier
schroederNurWennMeier(V) :- imVorstand(schroeder,V), imVorstand(meier,V).
schroederNurWennMeier(V) :- nichtImVorstand(schroeder,V).
40
41
42
43
44
45
% Meier nur wenn Schulz nicht Schriftführer
meierOhneSchulz(vorstand(V,K,schulz)) :nichtImVorstand(meier,vorstand(V,K,schulz)).
meierOhneSchulz(vorstand(_,_,S)) :verschieden(S,schulz).
46
47
48
49
50
51
% Schulz nur wenn Schröder nicht Vorsitzender
schulzOhneSchroeder(vorstand(schroeder,K,S)) :nichtImVorstand(schulz,vorstand(schroeder,K,S)).
schulzOhneSchroeder(vorstand(V,_,_)) :verschieden(V,schroeder).
52
53
54
55
56
57
58
59
60
% Lösen
loese(V) :- allesKandidaten(V),
alleVerschieden(V),
meierOderMueller(V),
muellerNurWennSchulz(V),
schroederNurWennMeier(V),
meierOhneSchulz(V),
schulzOhneSchroeder(V).
Als Lösungen ergeben sich:
?- loese(V).
V = vorstand(meier, schulz, schroeder) ;
V = vorstand(schulz, meier, schroeder) ;
V = vorstand(schulz, schroeder, meier) ;
false.
3 - Sprache eines Automaten
Wir geben noch einmal den Datentypen sowie drei Testautomaten an.
1
2
3
data State = State
[(Char, State)] -- transitions
Bool
-- final state?
4
5
6
7
8
9
10
11
-- accepts any word that contains "baa"
containsBAA :: State
containsBAA = let q0 = State [('a', q0),
q1 = State [('a', q2),
q2 = State [('a', q3),
q3 = State [('a', q3),
in q0
('b',
('b',
('b',
('b',
12
13
14
15
16
17
-- accepts any number of repetitions of "ab"
manyAB :: State
manyAB = let q0 = State [('a', q1)] True
q1 = State [('b', q0)] False
in q0
5
q1)]
q1)]
q1)]
q3)]
False
False
False
True
18
19
20
21
22
23
24
25
26
27
28
29
30
-- accepts only the word
haskell :: State
haskell = let q0 = State
q1 = State
q2 = State
q3 = State
q4 = State
q5 = State
q6 = State
q7 = State
q8 = State
in q0
"haskell"
[('h',
[('a',
[('s',
[('k',
[('e',
[('l',
[('l',
[('l',
[]
q1)]
q2)]
q3)]
q4)]
q5)]
q6)]
q7)]
q8)]
False
False
False
False
False
False
False
False
True
In der Implementierung nutzen wir eine Hilfsfunktion, die eine Liste von Paaren durchsucht, wobei jedes Paar
aus einem Zustand und dem Wortprefix besteht, durch das man zu diesem Zustand gelangt ist.
31
32
33
34
language :: State -> [String]
language s = lang [(s, [])]
where
lang :: [(State, String)] -> [String]
Ist die Liste leer, so gibt es keine betrachteten Zustände mehr, wir fügen keine Wörter hinzu.
35
lang [] = []
Für die nicht-leere Liste übernehmen wir zunächst die Wörter für die Endzustände, bevor wir die Wörter
anhand der jeweiligen Transitionen verlängern und die Folgezustände berücksichtigen. Dies kann man sehr
elegant mittels zweier List-Comprehensions ausdrücken, natürlich geht dies aber auch mittels map und filter
(1. Comprehension) bzw. concatMap (2. Comprehension).
36
37
lang ps = [ w | (State _ True, w) <- ps]
++ lang [ (s', w ++ [c]) | (State ts _, w) <- ps, (c, s') <- ts ]
Allerdings ist diese Lösung etwas ineffizient, da bei jeder Verlängerung eines Wortes um einen Buchstaben
das gesamte Wort durchlaufen werden muss. Dadurch ergeben sich für ein Wort der Länge n insgesamt
(n − 1) + (n − 2) + · · · + 1 Aufrufe von (++), was einer quadratischen Laufzeit entspricht.
Ein beliebter Trick ist hier, die Liste zunächst verkehrt herum aufzubauen, um sie erst ganz am Ende umzudrehen,
womit sich eine lineare Laufzeit ergibt.
38
39
40
41
42
43
language' :: State -> [String]
language' s = lang' [(s, [])]
where
lang' [] = []
lang' ps = [ reverse w | (State _ True, w) <- ps]
++ lang' [ (s', c:w) | (State ts _, w) <- ps, (c, s') <- ts ]
Zuguterletzt testen wir unsere Implementierung noch:
ghci> take 10 $ language containsBAA
["baa","abaa","baaa","baab","bbaa","aabaa","abaaa","abaab","abbaa","baaaa"]
ghci> take 10 $ language manyAB
["","ab","abab","ababab","abababab","ababababab","abababababab","ababababababab","abababababababab","ab
ghci> take 10 $ language haskell
["haskelll"]
Der letzte Aufruf würde übrigens nicht terminieren, wenn wir nicht den Spezialfall von lang für die leere Liste
implementiert hätten.
Der Laufzeitunterschied zwischen language und language' kann für große Wörter durchaus signifikant werden:
6
ghci> :set +s
ghci> language manyAB !! 10000
...
(20.74 secs, 18178318376 bytes)
ghci> language' manyAB !! 10000
...
(0.07 secs, 36044200 bytes)
Man sieht also, dass es sich durchaus lohnt über Laufzeiten von Funktionen nachzudenken.
4 - Testen von Warteschlangen
Zunächst importieren wir die QuickCheck-Bibliothek:
1
import Test.QuickCheck
Anschließend fügen wir die Show- und Arbitrary-Instanzen hinzu.
2
3
data Queue a = Queue [a] [a]
deriving Show
4
5
6
7
8
9
instance Arbitrary a => Arbitrary (Queue a) where
arbitrary = do
xs <- arbitrary
ys <- arbitrary
return (Queue xs ys)
Wir rekapitulieren noch einmal die Implementierung:
10
11
12
13
-- smart
queue ::
queue []
queue xs
constructor
[a] -> [a] -> Queue a
ys = Queue (reverse ys) ys
ys = Queue xs ys
14
15
16
17
-- empty queue
emptyQueue :: Queue a
emptyQueue = queue [] []
18
19
20
21
-- Is a queue empty?
isEmptyQueue :: Queue a -> Bool
isEmptyQueue (Queue _ ys) = null ys
22
23
24
25
-- add to a queue
enqueue :: a -> Queue a -> Queue a
enqueue x (Queue xs ys) = queue xs (x:ys)
26
27
28
29
30
-- get next element
next :: Queue a -> a
next (Queue (x:_) _) = x
next _
= error "Queue.next: empty queue"
31
32
33
34
35
-- remove first element
dequeue :: Queue a -> Queue a
dequeue (Queue (_:xs) ys) = queue ys xs
dequeue _
= error "Queue.dequeue: empty queue"
36
37
-- size of a queue
7
38
39
size :: Queue a -> Int
size (Queue xs ys) = length xs + length ys
40
41
42
43
-- invariant a queue should fulfill
invariant :: Queue a -> Bool
invariant (Queue xs ys) = not (null xs) || null ys
Es folgen die zu testenden Eigenschaften:
44
45
prop_queue :: [Int] -> [Int] -> Bool
prop_queue xs ys = invariant (queue xs ys)
46
47
48
prop_size_queue :: [Int] -> [Int] -> Bool
prop_size_queue xs ys = length xs + length ys == size (queue xs ys)
49
50
51
prop_emptyQueue :: Bool
prop_emptyQueue = invariant emptyQueue
52
53
54
prop_isEmptyQueue_emptyQueue :: Bool
prop_isEmptyQueue_emptyQueue = isEmptyQueue emptyQueue
55
56
57
prop_size_emptyQueue :: Bool
prop_size_emptyQueue = size emptyQueue == 0
58
59
60
prop_enqueue :: Int -> Queue Int -> Property
prop_enqueue x q = invariant q ==> invariant (enqueue x q)
61
62
63
prop_isEmptyQueue_enqueue :: Int -> Queue Int -> Property
prop_isEmptyQueue_enqueue x q = invariant q ==> not (isEmptyQueue (enqueue x q))
64
65
66
prop_size_enqueue :: Int -> Queue Int -> Property
prop_size_enqueue x q = invariant q ==> size (enqueue x q) == 1 + size q
67
68
69
prop_dequeue :: Queue Int -> Property
prop_dequeue q = invariant q && not (isEmptyQueue q) ==> invariant (dequeue q)
70
71
72
73
prop_size_dequeue :: Queue Int -> Property
prop_size_dequeue q = invariant q && not (isEmptyQueue q) ==>
size (dequeue q) == size q - 1
74
75
76
prop_next_enqueue_empty :: Int -> Bool
prop_next_enqueue_empty x = next (enqueue x emptyQueue) == x
77
78
79
80
prop_next_enqueue_Nonempty :: Int -> Queue Int -> Property
prop_next_enqueue_Nonempty x q = invariant q && not (isEmptyQueue q) ==>
next q == next (enqueue x q)
81
82
83
84
85
86
87
88
89
90
91
92
main :: IO ()
main = do
putStr "prop_queue
: "
quickCheck prop_queue
putStr "prop_size_queue
: "
quickCheck prop_size_queue
putStr "prop_emptyQueue
: "
quickCheck prop_emptyQueue
putStr "prop_isEmptyQueue_emptyQueue: "
quickCheck prop_isEmptyQueue_emptyQueue
putStr "prop_size_emptyQueue
: "
8
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
quickCheck prop_size_emptyQueue
putStr "prop_enqueue
:
quickCheck prop_enqueue
putStr "prop_isEmptyQueue_enqueue
:
quickCheck prop_isEmptyQueue_enqueue
putStr "prop_size_enqueue
:
quickCheck prop_size_enqueue
putStr "prop_dequeue
:
quickCheck prop_dequeue
putStr "prop_size_dequeue
:
quickCheck prop_size_dequeue
putStr "prop_next_enqueue_empty
:
quickCheck prop_next_enqueue_empty
putStr "prop_next_enqueue_Nonempty :
quickCheck prop_next_enqueue_Nonempty
"
"
"
"
"
"
"
Wir sehen, dass nicht alle Tests erfolgreich sind:
*Main> main
prop_queue
:
prop_size_queue
:
[]
[1]
prop_emptyQueue
:
prop_isEmptyQueue_emptyQueue:
prop_size_emptyQueue
:
prop_enqueue
:
prop_isEmptyQueue_enqueue
:
prop_size_enqueue
:
0
Queue [] []
prop_dequeue
:
prop_size_dequeue
:
prop_next_enqueue_empty
:
prop_next_enqueue_Nonempty :
OK, passed 100 tests.
Falsifiable, after 2 tests:
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
OK, passed 100 tests.
Falsifiable, after 0 tests:
OK,
OK,
OK,
OK,
passed
passed
passed
passed
100
100
100
100
tests.
tests.
tests.
tests.
5 - Europameisterschaft 2016
Die Relation spieltFuer(Spieler, Land):
1
2
3
4
5
6
7
8
spieltFuer(bale, wal).
spieltFuer(boateng, ger).
spieltFuer(buffon, ita).
spieltFuer(ibrahimovic, swe).
spieltFuer(kroos, ger).
spieltFuer(lewandowski, pol).
spieltFuer(pogba, fra).
spieltFuer(rooney, eng).
Die Relation spieltIn(Land, Gruppe):
9
10
11
12
13
spieltIn(eng,
spieltIn(ger,
spieltIn(fra,
spieltIn(ita,
spieltIn(pol,
gruppeB).
gruppeC).
gruppeA).
gruppeE).
gruppeC).
9
14
15
spieltIn(swe, gruppeE).
spieltIn(wal, gruppeB).
Die Relation vorrundenGegner(LandA, LandB):
16
17
18
19
20
21
vorrundenGegner(ger,
vorrundenGegner(pol,
vorrundenGegner(eng,
vorrundenGegner(wal,
vorrundenGegner(ita,
vorrundenGegner(swe,
pol).
ger).
wal).
eng).
swe).
ita).
Die Relation spieltInGruppe(Spieler, Gruppe):
22
spieltInGruppe(S, G) :- spieltFuer(S, L), spieltIn(L, G).
und spieltGegen(Spieler1, Spieler2):
23
24
25
spieltGegen(S1, S2) :spieltFuer(S1, L1), spieltFuer(S2, L2),
vorrundenGegner(L1, L2).
Die Anfragen (mit swipl):
% Laden der Datei
?- ['4_EM.pl'].
% 4_EM.pl compiled 0.00 sec, 24 clauses
true.
% Spielt Lewandowski für Deutschland?
?- spieltFuer(lewandowski, ger).
false.
% Spielt Kroos für Deutschland?
?- spieltFuer(kroos, ger).
true.
% Spielt England in der Gruppe C?
?- spieltInGruppe(eng, gruppeC).
false.
% In welcher Gruppe spielt Wales?
?- spieltIn(wal, Gruppe).
Gruppe = gruppeB.
% Für welches Land spielt Rooney?
?- spieltFuer(rooney, Land).
Land = eng.
% Welche Spieler spielen für Deutschland?
?- spieltFuer(Spieler, ger).
Spieler = boateng ;
Spieler = kroos.
% Gegen welche Spieler spielt Rooney?
?- spieltGegen(rooney, Gegner).
Gegner = bale ;
false.
10
% Welche Spieler der Gruppe C sind bekannt?
?- spieltInGruppe(Spieler, gruppeC).
Spieler = boateng ;
Spieler = kroos ;
Spieler = lewandowski ;
false.
% Spielen Kroos und Bale in der Vorrunde gegeneinander?
?- spieltGegen(kroos, bale).
false.
% Welche Spieler in welchen Gruppen sind bekannt?
?- spieltInGruppe(S, G).
S = bale,
G = gruppeB ;
S = boateng,
G = gruppeC ;
S = buffon,
G = gruppeE ;
S = ibrahimovic,
G = gruppeE ;
S = kroos,
G = gruppeC ;
S = lewandowski,
G = gruppeC ;
S = pogba,
G = gruppeA ;
S = rooney,
G = gruppeB.
11
Herunterladen