-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathset5a.hs
367 lines (297 loc) · 11.3 KB
/
set5a.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
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
-- Exercise set 5a
--
-- * defining algebraic datatypes
-- * recursive datatypes
module Set5a where
import Mooc.Todo
------------------------------------------------------------------------------
-- Ex 1: Define the type Vehicle that has four constructors: Bike,
-- Bus, Tram and Train.
--
-- The constructors don't need any fields.
data Vehicle = Bike | Tram | Train | Bus
------------------------------------------------------------------------------
-- Ex 2: Define the type BusTicket that can represent values like these:
-- - SingleTicket
-- - MonthlyTicket "January"
-- - MonthlyTicket "December"
data BusTicket = SingleTicket | MonthlyTicket String
------------------------------------------------------------------------------
-- Ex 3: Here's the definition for a datatype ShoppingEntry that
-- represents an entry in a shopping basket. It has an item name (a
-- String), an item price (a Double) and a count (an Int). You'll also
-- find two examples of ShoppingEntry values.
--
-- Implement the functions totalPrice and buyOneMore below.
data ShoppingEntry = MkShoppingEntry String Double Int
deriving Show
threeApples :: ShoppingEntry
threeApples = MkShoppingEntry "Apple" 0.5 3
twoBananas :: ShoppingEntry
twoBananas = MkShoppingEntry "Banana" 1.1 2
-- totalPrice should return the total price for an entry
--
-- Hint: you'll probably need fromIntegral to convert the Int into a
-- Double
--
-- Examples:
-- totalPrice threeApples ==> 1.5
-- totalPrice twoBananas ==> 2.2
totalPrice :: ShoppingEntry -> Double
totalPrice (MkShoppingEntry _ price count) = price * (fromIntegral count)
-- buyOneMore should increment the count in an entry by one
--
-- Example:
-- buyOneMore twoBananas ==> MkShoppingEntry "Banana" 1.1 3
buyOneMore :: ShoppingEntry -> ShoppingEntry
buyOneMore (MkShoppingEntry name price count)= MkShoppingEntry name price (count + 1)
------------------------------------------------------------------------------
-- Ex 4: define a datatype Person, which should contain the age (an
-- Int) and the name (a String) of a person.
--
-- Also define a Person value fred, and the functions getAge, getName,
-- setAge and setName (see below).
data Person = CreatePerson String Int
deriving Show
-- fred is a person whose name is Fred and age is 90
fred :: Person
fred = CreatePerson "Fred" 90
-- getName returns the name of the person
getName :: Person -> String
getName (CreatePerson name _) = name
-- getAge returns the age of the person
getAge :: Person -> Int
getAge (CreatePerson _ age) = age
-- setName takes a person and returns a new person with the name changed
setName :: String -> Person -> Person
setName name (CreatePerson _ age) = CreatePerson name age
-- setAge does likewise for age
setAge :: Int -> Person -> Person
setAge age (CreatePerson name _ ) = CreatePerson name age
------------------------------------------------------------------------------
-- Ex 5: define a datatype Position which contains two Int values, x
-- and y. Also define the functions below for operating on a Position.
--
-- Examples:
-- getY (up (up origin)) ==> 2
-- getX (up (right origin)) ==> 1
data Position = Position Int Int
-- origin is a Position value with x and y set to 0
origin :: Position
origin = Position 0 0
-- getX returns the x of a Position
getX :: Position -> Int
getX (Position x y) = x
-- getY returns the y of a position
getY :: Position -> Int
getY (Position x y) = y
-- up increases the y value of a position by one
up :: Position -> Position
up (Position x y)= Position x (y + 1)
-- right increases the x value of a position by one
right :: Position -> Position
right (Position x y)= Position (x + 1) y
------------------------------------------------------------------------------
-- Ex 6: Here's a datatype that represents a student. A student can
-- either be a freshman, a nth year student, or graduated.
data Student = Freshman | NthYear Int | Graduated
deriving (Show,Eq)
-- Implement the function study, which changes a Freshman into a 1st
-- year student, a 1st year student into a 2nd year student, and so
-- on. A 7th year student gets changed to a graduated student. A
-- graduated student stays graduated even if he studies.
study :: Student -> Student
study (Graduated) = Graduated
study (Freshman) = NthYear 1
study (NthYear 7) = Graduated
study (NthYear n) = NthYear (n + 1)
------------------------------------------------------------------------------
-- Ex 7: define a datatype UpDown that represents a counter that can
-- either be in increasing or decreasing mode. Also implement the
-- functions zero, toggle, tick and get below.
--
-- NB! Define _two_ constructors for your datatype (feel free to name the
-- constructors however you want)
--
-- Examples:
--
-- get (tick zero)
-- ==> 1
-- get (tick (tick zero))
-- ==> 2
-- get (tick (tick (toggle (tick zero))))
-- ==> -1
data UpDown = Increasing Int | Decreasing Int
-- zero is an increasing counter with value 0
zero :: UpDown
zero = Increasing 0
-- get returns the counter value
get :: UpDown -> Int
get (Increasing x) = x
get (Decreasing x) = x
-- tick increases an increasing counter by one or decreases a
-- decreasing counter by one
tick :: UpDown -> UpDown
tick (Increasing x) = (Increasing (x+1))
tick (Decreasing x) = (Decreasing (x-1))
-- toggle changes an increasing counter into a decreasing counter and
-- vice versa
toggle :: UpDown -> UpDown
toggle (Increasing x) = Decreasing x
toggle (Decreasing x) = Increasing x
------------------------------------------------------------------------------
-- Ex 8: you'll find a Color datatype below. It has the three basic
-- colours Red, Green and Blue, and two color transformations, Mix and
-- Invert.
--
-- Mix means the average of the two colors in each rgb channel.
--
-- Invert means subtracting all rgb values from 1.
--
-- Implement the function rgb :: Color -> [Double] that returns a list
-- of length three that represents the rgb value of the given color.
--
-- Examples:
--
-- rgb Red ==> [1,0,0]
-- rgb Green ==> [0,1,0]
-- rgb Blue ==> [0,0,1]
--
-- rgb (Mix Red Green) ==> [0.5,0.5,0]
-- rgb (Mix Red (Mix Red Green)) ==> [0.75,0.25,0]
-- rgb (Invert Red) ==> [0,1,1]
-- rgb (Invert (Mix Red (Mix Red Green))) ==> [0.25,0.75,1]
-- rgb (Mix (Invert Red) (Invert Green)) ==> [0.5,0.5,1]
data Color = Red | Green | Blue | Mix Color Color | Invert Color
deriving Show
rgb :: Color -> [Double]
rgb Red = [1,0,0]
rgb Green = [0,1,0]
rgb Blue = [0,0,1]
rgb (Invert x) = map (\x -> abs (1-x)) (rgb x)
rgb (Mix a b ) = zipWith (\x y -> (x + y ) / 2) (rgb a) (rgb b)
-- rgb (Mix Red)
------------------------------------------------------------------------------
-- Ex 9: define a parameterized datatype OneOrTwo that contains one or
-- two values of the given type. The constructors should be called One and Two.
--
data OneOrTwo a = One a | Two a a
-- Examples:
-- One True :: OneOrTwo Bool
-- Two "cat" "dog" :: OneOrTwo String
------------------------------------------------------------------------------
-- Ex 10: define a recursive datatype KeyVals for storing a set of
-- key-value pairs. There should be two constructors: Empty and Pair.
--
-- Empty represents an empty collection. It should have no fields.
--
-- Pair should have three fields, one for the key, one for the value,
-- and one for the rest of the collection (of type KeyVals)
--
-- The KeyVals datatype is parameterized by the key type k and
-- the value type v.
--
-- For example:
--
-- Pair "cat" True (Pair "dog" False Empty) :: KeyVals String Bool
--
-- Also define the functions toList and fromList that convert between
-- KeyVals and lists of pairs.
data KeyVals k v = Empty | Pair k v (KeyVals k v)
deriving Show
toList :: KeyVals k v -> [(k,v)]
toList Empty = []
toList (Pair k v l) = (k,v):toList l
fromList :: [(k,v)] -> KeyVals k v
fromList [] = Empty
fromList ((k,v):xs) = Pair k v (fromList xs)
------------------------------------------------------------------------------
-- Ex 11: The data type Nat is the so called Peano
-- representation for natural numbers. Define functions fromNat and
-- toNat that convert natural numbers to Ints and vice versa.
--
-- Examples:
-- fromNat (PlusOne (PlusOne (PlusOne Zero))) ==> 3
-- toNat 3 ==> Just (PlusOne (PlusOne (PlusOne Zero)))
-- toNat (-3) ==> Nothing
--
data Nat = Zero | PlusOne Nat
deriving (Show,Eq)
fromNat :: Nat -> Int
fromNat Zero = 0
fromNat (PlusOne n) = 1 + fromNat n
toNat :: Int ->Maybe Nat
toNat n | n < 0 = Nothing
| otherwise = Just (toNat' n)
toNat' :: Int ->Nat
toNat' 0 = Zero
toNat' n = PlusOne (toNat' (n-1))
-- toNat n = Just (PlusOne (toNat (n - 1)))
------------------------------------------------------------------------------
-- Ex 12: While pleasingly simple in its definition, the Nat datatype is not
-- very efficient computationally. Instead of the unary Peano natural numbers,
-- computers use binary numbers.
--
-- Binary numbers are like decimal numbers, except that binary numbers have
-- only two digits (called bits), 0 and 1. The table below gives some
-- examples:
--
-- decimal | binary
-- --------+-------
-- 0 | 0
-- 1 | 1
-- 2 | 10
-- 7 | 111
-- 44 | 101100
--
-- For allowing arbitrarily long binary numbers, our representation, the
-- datatype Bin, includes a special End constructor for denoting the end of
-- the binary number. In order to make computation with Bin easier, the bits
-- are represented in increasing order by significance (i.e. "backwards").
-- Consider the Bin numbers O (I (I End)), representing 110 in binary or
-- 6 in decimal, and I (I (O End)) that represents 011 in binary or 3 in
-- decimal. The most significant (last) bit, the bit I, of O (I (I End)) is
-- greater than the bit O, which is the most significant bit of I (I (O End)).
-- Therefore, O (I (I End)) is greater than I (I (O End)).
--
-- Your task is to write functions prettyPrint, fromBin, and toBin that
-- convert Bin to human-readable string, Bin to Int, and Int to Bin
-- respectively.
--
-- Examples:
-- prettyPrint End ==> ""
-- prettyPrint (O End) ==> "0"
-- prettyPrint (I End) ==> "1"
-- prettyPrint (O (O (I (O (I End))))) ==> "10100"
-- map fromBin [O End, I End, O (I End), I (I End), O (O (I End)),
-- I (O (I End))]
-- ==> [0, 1, 2, 3, 4, 5]
-- fromBin (I (I (O (O (I (O (I (O End)))))))) ==> 83
-- fromBin (I (I (O (O (I (O (I End))))))) ==> 83
-- map toBin [0..5] ==>
-- [O End,I End,O (I End),I (I End),O (O (I End)),I (O (I End))]
-- toBin 57 ==> I (O (O (I (I (I End)))))
--
-- Challenge: Can you implement toBin by directly converting its input into a
-- sequence of bits instead of repeatedly applying inc?
--
data Bin = End | O Bin | I Bin
deriving (Show, Eq)
-- This function increments a binary number by one.
inc :: Bin -> Bin
inc End = I End
inc (O b) = I b
inc (I b) = O (inc b)
prettyPrint :: Bin -> String
prettyPrint End = ""
prettyPrint (O b)= prettyPrint b ++ "0"
prettyPrint (I b)= prettyPrint b ++ "1"
fromBin :: Bin -> Int
fromBin b = fromBin' b 0
fromBin' :: Bin -> Int -> Int
fromBin' End curr = 0
fromBin' (I x) n = (1 * (2 ^ n) ) + (fromBin' x (n + 1))
fromBin' (O x) n = (0 * (2 ^ n) ) + (fromBin' x (n + 1))
toBin :: Int -> Bin
toBin 0 = O End
toBin n = inc (toBin (n-1))