forked from opqdonut/ifp2018-exercises
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathW6.hs
557 lines (491 loc) · 16.7 KB
/
W6.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
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
module W6 where
import Control.Monad
import Control.Monad.Trans.State
import Data.Char
import Data.List
-- Week 6: Monads
------------------------------------------------------------------------------
-- Ex 1: Here's the ?> chaining operator from the lecture:
(?>) :: Maybe a -> (a -> Maybe b) -> Maybe b
Nothing ?> _ = Nothing -- In case of failure, propagate failure
Just x ?> f = f x -- In case of sucess, run the next computation
-- Your task is to help implement the function readName that given a
-- string like "Forename Surname" produces the pair ("Forename",
-- "Surname"). readName should fail (return Nothing) in the following
-- cases:
--
-- 1. the input string doesn't contain a space
-- 2. both the names are the same
-- 3. one of the names doesn't start with a capital letter
--
-- The function readNames has already been implemented using ?>. You
-- need to define the functions split, checkNumber and checkCapitals
-- so that readNames works correctly.
-- DO NOT touch this definition!
readNames :: String -> Maybe (String,String)
readNames s =
split s
?>
checkDuplicate
?>
checkCapitals
-- split should split a string into two words. If the input doesn't
-- contain a space, Nothing should be returned
--
-- (NB! There are obviously other corner cases like the inputs " " and
-- "a b c", but you don't need to worry about those here)
split :: String -> Maybe (String,String)
split s =
case elemIndex ' ' s of
Nothing -> Nothing
Just i -> Just $ (\(a, b) -> (a, tail b)) (splitAt i s)
-- checkDuplacate should take a pair of two strings and return Nothing
-- if they are the same. Otherwise the strings are returned.
checkDuplicate :: (String, String) -> Maybe (String, String)
checkDuplicate (for,sur) =
if for == sur
then Nothing
else Just (for, sur)
-- checkCapitals should take a pair of two strings and return them
-- unchanged if both start with a capital letter. Otherwise Nothing is
-- returned.
checkCapitals :: (String, String) -> Maybe (String, String)
checkCapitals (for,sur) =
let isCaps x = isUpper $ x!!0
in if isCaps for && isCaps sur
then Just (for, sur)
else Nothing
------------------------------------------------------------------------------
-- Ex 2: implement a function myDrop that works just like drop, but
-- 1. the arguments are of types Maybe Int and Maybe [a]
-- 2. if either of the arguments is Nothing, Nothing is returned
-- 3. if the Int is larger than the length of the list or negative,
-- Nothing is returned
--
-- Use the Maybe monad, i.e. the >>= operator or do-notation.
--
-- DO NOT use pattern matching for Maybe.
--
-- Examples:
-- myDrop (Just 1) (Just [5,6,7])
-- ==> Just [6,7]
-- myDrop Nothing (Just [5,6,7])
-- ==> Nothing
-- myDrop (Just 2) Nothing
-- ==> Nothing
-- myDrop (Just 4) (Just [5,6,7])
-- ==> Nothing
myDrop :: Maybe Int -> Maybe [a] -> Maybe [a]
myDrop mi ml =
mi >>=
\x -> ml >>=
\y -> if x >= 0 && x <= (length y)
then return (drop x y)
else Nothing
------------------------------------------------------------------------------
-- Ex 3: given a list of values and a list of indices, return the sum
-- of the values in the given indices. You should fail if any of the
-- indices is too large or too small.
--
-- Use the Maybe monad, i.e. the >>= operator or do-notation.
--
-- DO NOT use pattern matching for Maybe.
--
-- Hint! implementa a function safeIndex :: [a] -> Int -> Maybe a
--
-- Examples:
-- selectSum [2,7,5,3,9] [0,2,4]
-- Just 16
-- selectSum [2,7,5,3,9] [0,2,5]
-- Nothing
selectSum :: Num a => [a] -> [Int] -> Maybe a
selectSum xs is = do
ys <- return $ map (safeIndex xs) is
zs <- return $ sequence ys
fmap sum zs
where safeIndex l i = if i >= 0 && i < (length l)
then Just (l!!i)
else Nothing
------------------------------------------------------------------------------
-- Ex 4: below you'll find the Logger monad from the lectures.
--
-- Your task is to implement a function binom that computes binomial
-- coefficients recursively with the following formulae:
--
-- B(n,0) = 1
-- B(0,k) = 0, when k>0
-- B(n,k) = B(n-1,k-1) + B(n-1,k)
--
-- Every call to the function should be logged as "B(n,k)".
-- Invocations should be logged in execution order.
--
-- Examples:
-- binom 0 0 ==> Logger ["B(0,0)"] 1
-- binom 0 7 ==> Logger ["B(0,7)"] 0
-- binom 1 1 ==> Logger ["B(0,0)","B(0,1)","B(1,1)"] 1
-- binom 2 2 ==> Logger ["B(0,0)","B(0,1)","B(1,1)","B(0,1)","B(0,2)","B(1,2)","B(2,2)"] 1
data Logger a = Logger [String] a
deriving Show
instance Functor Logger where
fmap f (Logger l a) = Logger l (f a)
instance Monad Logger where
return x = Logger [] x
Logger la a >>= f = Logger (la++lb) b
where Logger lb b = f a
-- Disregard this instance. In recent versions of the Haskell standard
-- library, all Monads must also be Applicative. This course doesn't
-- really cover Applicative.
instance Applicative Logger where
pure = return
(<*>) = ap
msg :: String -> Logger ()
msg s = Logger [s] ()
formatMsg :: Integer -> Integer -> String
formatMsg n k = "B(" ++ (show n) ++ "," ++ (show k) ++ ")"
-- Implement this:
binom :: Integer -> Integer -> Logger Integer
binom n 0 = do
msg $ formatMsg n 0
return 1
binom 0 k = do
msg $ formatMsg 0 k
return 0
binom n k = do
x <- binom (n-1) (k-1)
y <- binom (n-1) k
msg $ formatMsg n k
return $ x + y
------------------------------------------------------------------------------
-- Ex 5: using the State monad, write the operation update that first
-- multiplies the state by 2 and then adds one to it. The state has
-- type Int.
--
-- Example:
-- runState update 3
-- ==> ((),7)
update :: State Int ()
update = modify $ \x -> x*2 + 1
------------------------------------------------------------------------------
-- Ex 6: using the State monad, walk through a list and add up all the
-- elements in the state. Additionally you should return the length of
-- the list.
--
-- Do this by implementing a recursive State operation lengthAndSum.
-- DO NOT use the functions length or filter.
--
-- Example:
-- runState (lengthAndSum [1,2,3,4]) 0
-- ==> (4,10)
lengthAndSum :: [Int] -> State Int Int
lengthAndSum [] = return 0
lengthAndSum (x:xs) = do
i <- lengthAndSum xs
modify $ \x0 -> x + x0
return $ i + 1
------------------------------------------------------------------------------
-- Ex 7: Let's use a state of type [a] to keep track of which elements
-- we've seen an odd number of times (1, 3, 5, ...)
--
-- Implement the State operation oddUpdate that updates the state,
-- given an element. In other words, if the element is in the state,
-- remove it, and if it is not in the state, add it.
--
-- Examples:
-- runState (oddUpdate 1) [] ==> ((),[1])
-- runState (oddUpdate 1) [1,2,3] ==> ((),[2,3])
--
-- PS. Order of the list in the state doesn't matter
oddUpdate :: Eq a => a -> State [a] ()
oddUpdate x = modify $ \xs -> if elem x xs then delete x xs else x:xs
------------------------------------------------------------------------------
-- Ex 8: Define the operation oddsOp, so that the function odds
-- returns all the elements of a list that occur an odd number of
-- times.
--
-- Use the oddUpdate operation you just defined.
--
-- Examples:
-- odds [] ==> []
-- odds [1,2,3] ==> [1,2,3]
-- odds [1,1,2] ==> [2]
--
-- PS. Order of the returned list doesn't matter
odds :: Eq a => [a] -> [a]
odds xs = snd (runState (oddsOp xs) [])
oddsOp :: Eq a => [a] -> State [a] ()
oddsOp [] = return ()
oddsOp (x:xs) = oddsOp xs >> oddUpdate x
------------------------------------------------------------------------------
-- Ex 9: implement the function ifM, that takes three monadic
-- operations. If the first of the operations returns True, the second
-- operation should be run. Otherwise the third operation should be
-- run.
--
-- Examples (test is defined below):
-- runState (put 11 >> ifM test (return 'a') (return 'b')) 0
-- ==> ('b',11)
-- runState (put 9 >> ifM test (return 'a') (return 'b')) 0
-- ==> ('a',9)
test :: State Int Bool
test = do
x <- get
return (x<10)
ifM :: Monad m => m Bool -> m a -> m a -> m a
ifM opBool opThen opElse = do
x <- opBool
if x then opThen else opElse
------------------------------------------------------------------------------
-- Ex 10: the standard library function Control.Monad.mapM defines a
-- monadic map operation. Some examples of using it (safeDiv is defined
-- below):
--
-- mapM (safeDiv 10.0) [1.0,5.0,2.0] => Just [10.0,2.0,5.0]
-- mapM (safeDiv 10.0) [1.0,0.0,2.0] => Nothing
--
-- Your task is to implement the function mapM2 that works like mapM,
-- but there are two lists and the operation takes two arguments. If
-- the lists are of different lists, you can stop processing them once
-- the shorter one ends.
--
-- Examples:
-- mapM2 (\x y -> Just (x+y)) [1,2,3] [6,7]
-- ==> Just [7,9]
-- runState (mapM2 (\x y -> if x then modify (+y) else return () ) [True,False,True] [1,2,4]) 0
-- ==> ([(),(),()],5)
safeDiv :: Double -> Double -> Maybe Double
safeDiv x 0.0 = Nothing
safeDiv x y = Just (x/y)
mapM2 :: Monad m => (a -> b -> m c) -> [a] -> [b] -> m [c]
mapM2 op [] _ = return []
mapM2 op _ [] = return []
mapM2 op (x:xs) (y:ys) = do
rs <- (mapM2 op xs ys)
r <- op x y
return $ r:rs
------------------------------------------------------------------------------
-- Ex 11&12: Funnykiztan has cities that are named with by integers
-- 0 .. n-1. Some cities are connected by roads. Your task is to find
-- out if you can can get from city A to city B by following the
-- roads.
--
-- The road network is given as an adjacency list, which means a list
-- of lists [[Int]] where the i'th list gives the cities to which city
-- i has a road to.
--
-- For example the road network:
--
-- 0--1
-- |\ |
-- | \|
-- 2--3
--
-- would be represented as:
-- [[1,2,3]
-- ,[0,3]
-- ,[0,3]
-- ,[0,1,2]]
--
-- Below you'll find the function routeExists that solves the task.
-- However a very important piece of the function, the helper function
-- dfs is still unimplemented.
--
-- The function dfs is intended to run a Depth-First Search. If you
-- don't know what this means, have a look in wikipedia.
--
-- Simply put, dfs uses roads to travel from city to city, using a
-- state of type [Int] to keep track of which cities have been
-- visited. This is important because the road network will have
-- cycles.
--
-- Examples:
-- routeExists example1 0 2 ==> True
-- routeExists example2 0 2 ==> True
-- routeExists example2 3 5 ==> False
-- runState (dfs example2 0) [] ==> ((),[2,3,1,0])
-- When 1 and 2 have already been visited, dfs won't proceed to city 3:
-- runState (dfs example1 0) [1,2] ==> ((),[0,1,2])
--
-- A word on tests. The tests first test the function dfs in a couple
-- of simple situations. After this they test the function routeExists
-- more extensively. The tests look at the state produced by dfs but
-- do not care in which order it is.
-- Three cities, each connected to the two others
example1 :: [[Int]]
example1 = [[1,2]
,[0,2]
,[0,1]]
-- A more two-part network:
--
-- 0 -- 1
-- | | 4 -- 5
-- | |
-- 2 -- 3
example2 :: [[Int]]
example2 = [[1,2]
,[0,3]
,[0,3]
,[1,2]
,[5]
,[4]]
routeExists :: [[Int]] -> Int -> Int -> Bool
routeExists cities i j = j `elem` execState (dfs cities i) []
dfs :: [[Int]] -> Int -> State [Int] ()
dfs cities i = do
modify $ \x -> if not $ elem i x then i:x else x
visited <- get
mapM_ (dfs cities) $ filter (\x -> not $ elem x visited) $ cities!!i
return ()
------------------------------------------------------------------------------
-- Ex 13: define the function orderedPairs that returns all pairs
-- (i,j) such that i<j and i occurs in the given list before j.
--
-- Use the list monad!
--
-- Examples:
-- orderedPairs [1,3,2,4]
-- ==> [(1,3),(1,2),(1,4),(3,4),(2,4)]
--
-- PS. once again the tests don't care about the order of results
orderedPairs :: [Int] -> [(Int,Int)]
orderedPairs xs = do
a <- xs
b <- xs
if (a<b) && (elemIndex a xs) < (Just $ last $ elemIndices b xs) then [(a,b)] else []
------------------------------------------------------------------------------
-- Ex 14: Using the list monad, produce a list of all pairs of
-- _different_ elements in a given list.
--
-- You can assume the list contains no duplicates.
--
-- DO NOT use map, list pattern matching. Use >>= or do notation.
--
-- Examples:
-- pairs [1] ==> []
-- pairs [1,2] ==> [(1,2),(2,1)]
-- pairs [1,2,3] ==> [(1,2),(1,3),(2,1),(2,3),(3,1),(3,2)]
--
-- PS. the order of the returned list does not matter
pairs :: Eq a => [a] -> [(a,a)]
pairs xs = do
a <- xs
b <- xs
if (a/=b) then [(a,b)] else []
------------------------------------------------------------------------------
-- Ex 15: the standard library defines the function
--
-- foldM :: (Monad m) => (a -> b -> m a) -> a -> [b] -> m a
--
-- This function behaves like foldr, but the operation used is
-- monadic. foldM f acc xs works by running f for each element in xs,
-- giving it also the result of the previous invocation of f.
--
-- Your task is to implement the functions fsum so that the
-- function sumNotTwice works.
--
-- sumNotTwice computes the sum of a list, but ignores duplicated
-- elements. That is, only the first occurrence of a given number is
-- counted.
--
-- Examples:
-- sumNotTwice [1,1,1,1,1] ==> 1
-- sumNotTwice [5,-2,5] ==> 3
-- sumNotTwice [1,2,-2,3] ==> 4
sumNotTwice :: [Int] -> Int
sumNotTwice xs = fst $ runState (foldM fsum 0 xs) []
fsum :: Int -> Int -> State [Int] Int
fsum acc x = do
r <- get
modify (x:)
return $ if elem x r
then acc
else acc+x
------------------------------------------------------------------------------
-- Ex 16: here is the Result type from last week. Implement a Monad
-- Result instance that behaves roughly like the Monad Maybe instance.
--
-- That is,
-- 1. MkResults behave like Just
-- 2. If part of computation produces NoResult, the whole computation
-- produces NoResult (just like Nothing)
-- 3. Similarly, if we get a Failure "reason" value, the whole
-- computation produces Failure "reason"
--
-- Additionally, the method "fail" of the Monad type class should
-- produce a Failure.
--
-- Examples:
-- MkResult 1 >> Failure "boom" >> MkResult 2
-- ==> Failure "boom"
-- MkResult 1 >> NoResult >> Failure "not reached"
-- ==> NoResult
-- MkResult 1 >>= (\x -> MkResult (x+1))
-- ==> MkResult 2
data Result a = MkResult a | NoResult | Failure String deriving (Show,Eq)
-- A straightforward Functor instance
instance Functor Result where
fmap f (MkResult a) = MkResult (f a)
fmap _ NoResult = NoResult
fmap _ (Failure s) = Failure s
-- Disregard this instance. In recent versions of the Haskell standard
-- library, all Monads must also be Applicative. These exercises don't
-- really cover Applicative.
instance Applicative Result where
pure = return
(<*>) = ap
instance Monad Result where
return = MkResult
fail s = Failure s
MkResult x >>= k = k x
NoResult >>= k = NoResult
Failure s >>= k = Failure s
------------------------------------------------------------------------------
-- Ex 17&18: Here is the type SL that combines the State and Logger
-- types. Implement an instance Monad SL, that behaves like the
-- combination of State and Logger. That is, state is propagated from
-- one operation to the next, and log messages are stored in the order
-- they are produced.
--
-- To simplify the type signatures, the type of the state has been set
-- to Int, instead of being a parameter like in the standard State
-- monad.
--
-- This is a tough one. Keep trying and you'll get it!
--
-- You might find it easier to start with the Functor instance
--
-- Examples:
-- runSL (fmap (+1) getSL) 13
-- ==> (14,13,[])
-- runSL (fmap (const True) (msgSL "hello")) 17
-- ==> (True,17,["hello"])
-- runSL (putSL 2 >> msgSL "hello" >> getSL) 0
-- ==> (2,2,["hello"])
-- runSL (replicateM_ 5 (modifySL (+1) >> getSL >>= \x -> msgSL ("got "++show x))) 1
-- ==> ((),6,["got 2","got 3","got 4","got 5","got 6"])
data SL a = SL (Int -> (a,Int,[String]))
-- Run an SL operation with the given starting state
runSL :: SL a -> Int -> (a,Int,[String])
runSL (SL f) state = f state
-- Write a log message
msgSL :: String -> SL ()
msgSL msg = SL (\s -> ((),s,[msg]))
-- Fetch the state
getSL :: SL Int
getSL = SL (\s -> (s,s,[]))
-- Overwrite the state
putSL :: Int -> SL ()
putSL s' = SL (\s -> ((),s',[]))
-- Modify the state
modifySL :: (Int->Int) -> SL ()
modifySL f = SL (\s -> ((),f s,[]))
instance Functor SL where
-- fmap f k = let (a, i, s) = runSL k 1
-- in SL (\x -> (f a, x+1, s))
-- again, disregard this
instance Applicative SL where
pure = return
(<*>) = ap
instance Monad SL where
-- return x = SL (\s -> (), x, [])
-- SL a >>= k =
-- implement return and >>=