-
Notifications
You must be signed in to change notification settings - Fork 3
/
Chapter5.hs
309 lines (200 loc) · 6.55 KB
/
Chapter5.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
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
-------------------------------------------------------------------------
--
-- Haskell: The Craft of Functional Programming, 3e
-- Simon Thompson
-- (c) Addison-Wesley, 1996-2011.
--
-- Chapter 5
--
-------------------------------------------------------------------------
module Chapter5 where
import Prelude hiding (id)
import Test.QuickCheck
import Data.Char
-- Data types: tuples and lists
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- Introducing tuples, lists and strings
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
type ShopItem = (String,Int)
type Basket = [ShopItem]
basket1 :: Basket
basket1 = [ ("Salt: 1kg",139) , ("Plain crisps",25) , ("Gin: 1lt",1099) ]
basket2 :: Basket
basket2 = []
basket3 :: Basket
basket3 = [ ("Salt: 1kg",139) , ("Plain crisps",25) , ("Plain crisps",25) ]
-- Tuple types
-- ^^^^^^^^^^^
-- Minimum and maximum of two integers.
minAndMax :: Integer -> Integer -> (Integer,Integer)
minAndMax x y
| x>=y = (y,x)
| otherwise = (x,y)
-- Adding a pair of intgers.
addPair :: (Integer,Integer) -> Integer
addPair (x,y) = x+y
-- Shifting around the structure of an ((Int,Int),Int).
shift :: ((Integer,Integer),Integer) -> (Integer,(Integer,Integer))
shift ((x,y),z) = (x,(y,z))
-- Selecting parts of a tuple
name :: ShopItem -> String
price :: ShopItem -> Int
name (n,p) = n
price (n,p) = p
-- Adding a pair using the built-in selectors, fst and snd.
addPair' :: (Integer,Integer) -> Integer
addPair' p = fst p + snd p
-- Fibonacci numbers: an efficient function, fastFib.
fibStep :: (Integer,Integer) -> (Integer,Integer)
fibStep (u,v) = (v,u+v)
fibPair :: Integer -> (Integer,Integer)
fibPair n
| n==0 = (0,1)
| otherwise = fibStep (fibPair (n-1))
fastFib :: Integer -> Integer
fastFib = fst . fibPair
fibTwoStep :: Integer -> Integer -> (Integer,Integer)
fibTwoStep x y = (y,x+y)
-- Introducing algebraic types
-- ^^^^^^^^^^^^^^^^^^^^^^^^^^^
-- We give a sequence of examples of increasing complexity ...
-- Product types
-- ^^^^^^^^^^^^^
-- A person is represented by their name and age ...
data People = Person Name Age
-- where Name and Age are the appropriate synonyms.
type Name = String
type Age = Int
jemima, ronnie :: People
jemima = Person "Electric Aunt Jemima" 77
ronnie = Person "Ronnie" 14
-- Turning a person into a string.
showPerson :: People -> String
showPerson (Person st n) = st ++ " -- " ++ show n
-- An alternative to Age,
data NewAge = Years Int
-- Alternatives
-- ^^^^^^^^^^^^
-- A shape in a simple geometrical program is either a circle or a
-- rectangle. These alternatives are given by the type
data Shape = Circle Float |
Rectangle Float Float
deriving (Eq,Ord,Show,Read)
shape1 = Circle 3.0
shape2 = Rectangle 45.9 87.6
-- Pattern matching allows us to define functions by cases, as in,
isRound :: Shape -> Bool
isRound (Circle _) = True
isRound (Rectangle _ _) = False
-- and also lets us use the components of the elements:
area :: Shape -> Float
area (Circle r) = pi*r*r
area (Rectangle h w) = h*w
-- Derived instances ...
-- data Season = Spring | Summer | Autumn | Winter
-- deriving (Eq,Ord,Enum,Show,Read)
-- Lists in Haskell
-- ^^^^^^^^^^^^^^^^
-- Various examples of lists
list1 :: [Integer]
list1 = [1,2,3,4,1,4]
list2 :: [Bool]
list2 = [True]
list3 :: String
list3 = ['a','a','b']
list4 :: String
list4 = "aab"
list5 :: [ Integer -> Integer ]
list5 = [fastFib,fastFib]
list6 :: [ [Integer] ]
list6 = [[12,2],[2,12],[]]
list7 :: [Integer]
list7 = [2 .. 7]
list8 :: [Float]
list8 = [3.1 .. 7.0]
list9 :: String
list9 = ['a' .. 'm']
list10 :: [Integer]
list10 = [7,6 .. 3]
list11 :: [Float]
list11 = [0.0,0.3 .. 1.0]
list12 :: String
list12 = ['a','c' .. 'n']
-- List comprehensions
-- ^^^^^^^^^^^^^^^^^^^
-- Examples of list comprehensions
ex :: [Integer]
ex = [2,4,7]
comp1 :: [Integer]
comp1 = [ 2*n | n<-ex]
comp2 :: [Bool]
comp2 = [ isEven n | n<-ex ]
isEven :: Integer -> Bool
isEven n = (n `mod` 2 == 0)
comp3 :: [Integer]
comp3 = [ 2*n | n <- ex , isEven n , n>3 ]
-- Add all the pairs in a list of pairs.
addPairs :: [(Integer,Integer)] -> [Integer]
addPairs pairList = [ m+n | (m,n) <- pairList ]
-- Return only the sums of pairs which are increasing.
addOrdPairs :: [(Integer,Integer)] -> [Integer]
addOrdPairs pairList = [ m+n | (m,n) <- pairList , m<n ]
-- Return only the digits in a String.
digits :: String -> String
digits st = [ ch | ch<-st , isDigit ch ]
-- Are all the integers in a list even? or odd?
allEven, allOdd :: [Integer] -> Bool
allEven xs = (xs == [x | x<-xs, isEven x])
allOdd xs = ([] == [x | x<-xs, isEven x])
-- Summing the radii of the circles in a list, ignores the other shapes
totalRadii :: [Shape] -> Float
totalRadii shapes = sum [r | Circle r <- shapes]
-- Extracting all the singletons in a list of integer lists,
-- ignoring the other lists.
sings :: [[Integer]] -> [Integer]
sings xss = [x | [x] <-xss ]
-- A library database
-- ^^^^^^^^^^^^^^^^^^
-- Types
type Person = String
type Book = String
type Database = [ (Person , Book) ]
-- An example database.
exampleBase :: Database
exampleBase
= [ ("Alice" , "Tintin") , ("Anna" , "Little Women") ,
("Alice" , "Asterix") , ("Rory" , "Tintin") ]
-- The books borrowed by a particular person in the given database.
books :: Database -> Person -> [Book]
books dBase findPerson
= [ book | (person,book) <- dBase , person==findPerson ]
-- Making a loan is done by adding a pair to the database.
makeLoan :: Database -> Person -> Book -> Database
makeLoan dBase pers bk = [ (pers,bk) ] ++ dBase
-- To return a loan.
returnLoan :: Database -> Person -> Book -> Database
returnLoan dBase pers bk
= [ pair | pair <- dBase , pair /= (pers,bk) ]
-- Testing the database.
-- Commented out because borrowed is not defined here.
-- test1 :: Bool
-- test1 = borrowed exampleBase "Asterix"
test2 :: Database
test2 = makeLoan exampleBase "Alice" "Rotten Romans"
-- QuickCheck properties for the database
-- Check that bk is in the list of loaned books to pers
-- after making the loan of book to pers
prop_db1 :: Database -> Person -> Book -> Bool
prop_db1 dBase pers bk =
elem bk loanedAfterLoan == True
where
afterLoan = makeLoan dBase pers bk
loanedAfterLoan = books afterLoan pers
-- Check that bk is not in the list of loaned books to pers
-- after returning the loan of book to pers
prop_db2 :: Database -> Person -> Book -> Bool
prop_db2 dBase pers bk =
elem bk loanedAfterReturn == False
where
afterReturn = returnLoan dBase pers bk
loanedAfterReturn = books afterReturn pers