-
Notifications
You must be signed in to change notification settings - Fork 3
/
Pictures.hs
256 lines (164 loc) · 5.87 KB
/
Pictures.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
-----------------------------------------------------------------------
-- Haskell: The Craft of Functional Programming
-- Simon Thompson
-- (c) Addison-Wesley, 1996-2010.
--
-- Pictures.hs
--
-- An implementation of a type of rectangular pictures
-- using lists of lists of characters.
-----------------------------------------------------------------------
-- The basics
-- ^^^^^^^^^^
module Pictures where
import Test.QuickCheck
type Picture = [[Char]]
-- The example used in Craft2e: a polygon which looks like a horse. Here
-- taken to be a 16 by 12 rectangle.
horse :: Picture
horse = [".......##...",
".....##..#..",
"...##.....#.",
"..#.......#.",
"..#...#...#.",
"..#...###.#.",
".#....#..##.",
"..#...#.....",
"...#...#....",
"....#..#....",
".....#.#....",
"......##...."]
-- Completely white and black pictures.
white :: Picture
white = ["......",
"......",
"......",
"......",
"......",
"......"]
black = ["######",
"######",
"######",
"######",
"######",
"######"]
-- Getting a picture onto the screen.
printPicture :: Picture -> IO ()
printPicture = putStr . concat . map (++"\n")
-- Transformations of pictures.
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Reflection in a vertical mirror.
flipV :: Picture -> Picture
flipV = map reverse
-- Reflection in a horizontal mirror.
flipH :: Picture -> Picture
flipH = reverse
-- Rotation through 180 degrees, by composing vertical and horizontal
-- reflection. Note that it can also be done by flipV.flipH, and that we
-- can prove equality of the two functions.
rotate :: Picture -> Picture
rotate = flipH . flipV
-- One picture above another. To maintain the rectangular property,
-- the pictures need to have the same width.
above :: Picture -> Picture -> Picture
above = (++)
-- One picture next to another. To maintain the rectangular property,
-- the pictures need to have the same height.
beside :: Picture -> Picture -> Picture
beside = zipWith (++)
-- Superimose one picture above another. Assume the pictures to be the same
-- size. The individual characters are combined using the combine function.
superimpose :: Picture -> Picture -> Picture
superimpose = zipWith (zipWith combine)
-- For the result to be '.' both components have to the '.'; otherwise
-- get the '#' character.
combine :: Char -> Char -> Char
combine topCh bottomCh
= if (topCh == '.' && bottomCh == '.')
then '.'
else '#'
-- Inverting the colours in a picture; done pointwise by invert...
invertColour :: Picture -> Picture
invertColour = map (map invert)
-- ... which works by making the result '.' unless the input is '.'.
invert :: Char -> Char
invert ch = if ch == '.' then '#' else '.'
-- Property
prop_rotate, prop_flipV, prop_flipH :: Picture -> Bool
prop_rotate pic = flipV (flipH pic) == flipH (flipV pic)
prop_flipV pic = flipV (flipV pic) == pic
prop_flipH pic = flipH (flipV pic) == pic
test_rotate, test_flipV, test_flipH :: Bool
test_rotate = flipV (flipH horse) == flipH (flipV horse)
test_flipV = flipV (flipV horse) == horse
test_flipH = flipH (flipV horse) == horse
-- More properties
prop_AboveFlipV pic1 pic2 =
flipV (pic1 `above` pic2) == (flipV pic1) `above` (flipV pic2)
prop_AboveFlipH pic1 pic2 = flipH (pic1 `above` pic2) == (flipH pic2) `above` (flipH pic1)
propAboveBeside1 nw ne sw se =
(nw `beside` ne) `above` (sw `beside` se)
==
(nw `above` sw) `beside` (ne `above` se)
propAboveBeside2 n s =
(n `beside` n) `above` (s `beside` s) == (n `above` s) `beside` (n `above` s)
propAboveBeside3 w e =
(w `beside` e) `above` (w `beside` e) == (w `above` w) `beside` (e `above` e)
propAboveBeside3Correct w e =
(rectangular w && rectangular e && height w == height e)
==>
(w `beside` e) `above` (w `beside` e)
==
(w `above` w) `beside` (e `above` e)
-- auxiliary properties and functions
notEmpty pic = pic /= []
rectangular pic =
notEmpty pic &&
and [ length first == length l | l <-rest ]
where
(first:rest) = pic
height, width :: Picture -> Int
height = length
width = length . head
size :: Picture -> (Int,Int)
size pic = (width pic, height pic)
propAboveBesideFull nw ne sw se =
(rectangular nw && rectangular ne && rectangular sw && rectangular se &&
size nw == size ne && size ne == size se && size se == size sw) ==>
(nw `beside` ne) `above` (sw `beside` se) == (nw `above` sw) `beside` (ne `above` se)
-- Using explicit generators ...
prop_1 = forAll (choose (1,10)) $ \x -> x/=x+(x::Int)
prop_2 = forAll (choose (1,10)) $ \x -> x/=(x::Int)
-- Generators suited to Pictures
-- chose either '.' or '#'
genChar :: Gen Char
genChar = oneof [return '.', return '#']
-- generate a list of length n each element from generator g.
genList :: Int -> Gen a -> Gen [a]
genList n g = sequence [ g | i<-[1..n] ]
-- generate a picture of given size using '.' and '#'
genSizedPicture :: Int -> Int -> Gen [String]
genSizedPicture height width =
sequence [ genList width genChar | i<-[1::Int .. height] ]
-- generate a picture of random size using '.' and '#'
genPicture :: Gen [String]
genPicture =
do
height <- choose (1,10)
width <- choose (1,10)
genSizedPicture height width
-- generate four pictures of the *same* random size using '.' and '#'
genFourPictures :: Gen ([String],[String],[String],[String])
genFourPictures =
do
height <- choose (1,10)
width <- choose (1,10)
nw <- genSizedPicture height width
ne <- genSizedPicture height width
sw <- genSizedPicture height width
se <- genSizedPicture height width
return (nw,ne,sw,se)
-- test that above and besides commute when used with four pictures
-- of the same size
prop_AboveBeside =
forAll genFourPictures $ \(nw,ne,sw,se) -> propAboveBeside1 nw ne sw se