forked from moocfi/haskell-mooc
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSet6.hs
300 lines (252 loc) · 9 KB
/
Set6.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
-- Exercise set 6: defining classes and instances
module Set6 where
import Mooc.Todo
import Data.Char (toLower)
------------------------------------------------------------------------------
-- Ex 1: define an Eq instance for the type Country below. You'll need
-- to use pattern matching.
data Country = Finland | Switzerland | Norway
deriving Show
instance Eq Country where
Finland == Finland = True
Switzerland == Switzerland = True
Norway == Norway = True
_ == _ = False
------------------------------------------------------------------------------
-- Ex 2: implement an Ord instance for Country so that
-- Finland <= Norway <= Switzerland
--
-- Remember minimal complete definitions!
instance Ord Country where
compare Finland Finland = EQ
compare Norway Norway = EQ
compare Switzerland Switzerland = EQ
compare Finland Norway = LT
compare Finland Switzerland = LT
compare Norway Switzerland = LT
compare Norway Finland = GT
compare Switzerland Finland = GT
compare Switzerland Norway = GT
x <= y = compare x y /= GT
x >= y = compare x y /= LT
x < y = compare x y == LT
x > y = compare x y == GT
min x y
| x <= y = x
| otherwise = y
max x y
| x >= y = x
| otherwise = y
------------------------------------------------------------------------------
-- Ex 3: Implement an Eq instance for the type Name which contains a String.
-- The Eq instance should ignore capitalization.
--
-- Hint: use the function Data.Char.toLower that has been imported for you.
--
-- Examples:
-- Name "Pekka" == Name "pekka" ==> True
-- Name "Pekka!" == Name "pekka" ==> False
data Name = Name String
deriving Show
instance Eq Name where
(==) (Name s1) (Name s2) = map toLower s1 == map toLower s2
------------------------------------------------------------------------------
-- Ex 4: here is a list type parameterized over the type it contains.
-- Implement an instance "Eq (List a)" that compares the lists element
-- by element.
--
-- Note how the instance needs an Eq a constraint. What happens if you
-- remove it?
data List a = Empty | LNode a (List a)
deriving Show
instance Eq a => Eq (List a) where
Empty == Empty = True
(LNode x xs) == (LNode y ys) = x == y && xs == ys
_ == _ = False
------------------------------------------------------------------------------
-- Ex 5: below you'll find two datatypes, Egg and Milk. Implement a
-- type class Price, containing a function price. The price function
-- should return the price of an item.
--
-- The prices should be as follows:
-- * chicken eggs cost 20
-- * chocolate eggs cost 30
-- * milk costs 15 per liter
--
-- Example:
-- price ChickenEgg ==> 20
data Egg = ChickenEgg | ChocolateEgg
deriving Show
data Milk = Milk Int -- amount in litres
deriving Show
-- Define the Price type class
class Price a where
price :: a -> Int
-- Define instances for Egg
instance Price Egg where
price ChickenEgg = 20
price ChocolateEgg = 30
-- Define an instance for Milk
instance Price Milk where
price (Milk liters) = 15 * liters
------------------------------------------------------------------------------
-- Ex 6: define the necessary instance hierarchy in order to be able
-- to compute these:
--
-- price (Just ChickenEgg) ==> 20
-- price [Milk 1, Milk 2] ==> 45
-- price [Just ChocolateEgg, Nothing, Just ChickenEgg] ==> 50
-- price [Nothing, Nothing, Just (Milk 1), Just (Milk 2)] ==> 45
instance (Price a) => Price (Maybe a) where
price Nothing = 0
price (Just x) = price x
instance (Price a) => Price [a] where
price xs = sum (map price xs)
------------------------------------------------------------------------------
-- Ex 7: below you'll find the datatype Number, which is either an
-- Integer, or a special value Infinite.
--
-- Implement an Ord instance so that finite Numbers compare normally,
-- and Infinite is greater than any other value.
data Number = Finite Integer | Infinite
deriving (Show,Eq)
instance Ord Number where
compare (Finite i1) (Finite i2) = compare i1 i2
compare (Finite _) Infinite = LT
compare Infinite (Finite _) = GT
compare Infinite Infinite = EQ
------------------------------------------------------------------------------
-- Ex 8: rational numbers have a numerator and a denominator that are
-- integers, usually separated by a horizontal bar or a slash:
--
-- numerator
-- ------------- == numerator / denominator
-- denominator
--
-- You may remember from school that two rationals a/b and c/d are
-- equal when a*d == b*c. Implement the Eq instance for rationals
-- using this definition.
--
-- You may assume in all exercises that the denominator is always
-- positive and nonzero.
--
-- Examples:
-- RationalNumber 4 5 == RationalNumber 4 5 ==> True
-- RationalNumber 12 15 == RationalNumber 4 5 ==> True
-- RationalNumber 13 15 == RationalNumber 4 5 ==> False
data RationalNumber = RationalNumber Integer Integer
deriving Show
instance Eq RationalNumber where
(RationalNumber a b) == (RationalNumber c d) = a * d == b * c
------------------------------------------------------------------------------
-- Ex 9: implement the function simplify, which simplifies a rational
-- number by removing common factors of the numerator and denominator.
-- In other words,
--
-- ca a
-- ---- ==> ---
-- cb b
--
-- As a concrete example,
--
-- 12 3 * 4 4
-- ---- == ------- ==> ---.
-- 15 3 * 5 5
--
-- Hint: Remember the function gcd?
simplify (RationalNumber a b) =
let commonFactor = gcd a b
in RationalNumber (a `div` commonFactor) (b `div` commonFactor)
------------------------------------------------------------------------------
-- Ex 10: implement the typeclass Num for RationalNumber. The results
-- of addition and multiplication must be simplified.
--
-- Reminders:
-- * negate x is 0-x
-- * abs is absolute value
-- * signum is -1, +1 or 0 depending on the sign of the input
--
-- Examples:
-- RationalNumber 1 3 + RationalNumber 1 6 ==> RationalNumber 1 2
-- RationalNumber 1 3 * RationalNumber 3 1 ==> RationalNumber 1 1
-- negate (RationalNumber 2 3) ==> RationalNumber (-2) 3
-- fromInteger 17 :: RationalNumber ==> RationalNumber 17 1
-- abs (RationalNumber (-3) 2) ==> RationalNumber 3 2
-- signum (RationalNumber (-3) 2) ==> RationalNumber (-1) 1
-- signum (RationalNumber 0 2) ==> RationalNumber 0 1
instance Num RationalNumber where
(RationalNumber a b) + (RationalNumber c d) =
simplify $ RationalNumber (a * d + b * c) (b * d)
(RationalNumber a b) * (RationalNumber c d) =
simplify $ RationalNumber (a * c) (b * d)
negate (RationalNumber a b) = RationalNumber (-a) b
abs (RationalNumber a b)
| a < 0 = RationalNumber (-a) b
| otherwise = RationalNumber a b
signum (RationalNumber a _)
| a > 0 = RationalNumber 1 1
| a < 0 = RationalNumber (-1) 1
| otherwise = RationalNumber 0 1
fromInteger x = RationalNumber x 1
------------------------------------------------------------------------------
-- Ex 11: a class for adding things. Define a class Addable with a
-- constant `zero` and a function `add`. Define instances of Addable
-- for Integers and lists. Numbers are added with the usual addition,
-- while lists are added by catenating them. Pick a value for `zero`
-- such that: `add zero x == x`
--
-- Examples:
-- add 1 2 ==> 3
-- add 1 zero ==> 1
-- add [1,2] [3,4] ==> [1,2,3,4]
-- add zero [True,False] ==> [True,False]
class Addable a where
zero :: a
add :: a -> a -> a
instance Addable Integer where
zero = 0
add x y = x + y
instance Addable [a] where
zero = []
add xs ys = xs ++ ys
------------------------------------------------------------------------------
-- Ex 12: cycling. Implement a type class Cycle that contains a
-- function `step` that cycles through the values of the type.
-- Implement instances for Color and Suit that work like this:
--
-- step Red ==> Green
-- step Green ==> Blue
-- step Blue ==> Red
--
-- The suit instance should cycle suits in the order Club, Spade,
-- Diamond, Heart, Club.
--
-- Also add a function `stepMany` to the class and give it a default
-- implementation using `step`. The function `stepMany` should take
-- multiple (determined by an Int argument) steps like this:
--
-- stepMany 2 Club ==> Diamond
-- stepMany 3 Diamond ==> Spade
--
-- The tests will test the Cycle class and your default implementation
-- of stepMany by adding an instance like this:
--
-- instance Cycle Int where
-- step = succ
data Color = Red | Green | Blue
deriving (Show, Eq)
data Suit = Club | Spade | Diamond | Heart
deriving (Show, Eq)
class Cycle a where
step :: a -> a
stepMany :: Int -> a -> a
stepMany n x = iterate step x !! n
instance Cycle Color where
step Red = Green
step Green = Blue
step Blue = Red
instance Cycle Suit where
step Club = Spade
step Spade = Diamond
step Diamond = Heart
step Heart = Club