forked from opqdonut/ifp2018-exercises
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathW7.hs
238 lines (203 loc) · 7.56 KB
/
W7.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
module W7 where
-- Final week!
--
-- Think of this as the exam. You must get 4/10 exercises to pass the course.
--
-- Good luck.
--
-- NB. Do not add any imports!
import Data.List
import Control.Monad
import Control.Monad.Trans.State
------------------------------------------------------------------------------
-- Ex 1: Count how many numbers in the input list are in the given
-- low-high range (inclusive)
--
-- Examples:
-- countRange 5 8 [] ==> 0
-- countRange 1 3 [1,2,3,4,5] ==> 3
countRange :: Int -> Int -> [Int] -> Int
countRange low high is = length $ filter (\x -> x <= high && x >= low) is
------------------------------------------------------------------------------
-- Ex 2: Build a string that looks like an n*m chessboard:
--
-- #.#.#.#.
-- .#.#.#.#
-- #.#.#.#.
--
-- Examples:
-- chess 1 1 ==> "#\n"
-- chess 3 5 ==> "#.#.#\n.#.#.\n#.#.#\n"
--
-- Hint: it's easier to see how the chess board looks like if you run
-- putStr (chess 3 5)
-- in GHCi
chess :: Int -> Int -> String
chess 0 i = ""
chess c i = chess' c i "#."
where chess' 0 _ _ = []
chess' c i squares = (take i $ cycle squares) ++
"\n" ++
chess' (c-1) i (reverse squares)
------------------------------------------------------------------------------
-- Ex 3: Implement the function palindromify that chops a character
-- off the front _and_ back of a string until the result is a
-- palindrome.
--
-- Examples:
-- palindromify "ab" ==> ""
-- palindromify "aaay" ==> "aa"
-- palindromify "xabbay" ==> "abba"
-- palindromify "abracacabra" ==> "acaca"
palindromify :: String -> String
palindromify str = if str == (reverse str)
then str
else palindromify $ tail . init $ str
------------------------------------------------------------------------------
-- Ex 4: Remove all repetitions of elements in a list. That is, if an
-- element occurs in the input list 2 or more times in a row, replace
-- this with 1 occurrence.
--
-- DO NOT use any library list functions like head, tail, (++) and so on.
-- USE ONLY recursion and pattern matching to process the list.
--
-- It's ok to use (==) or compare obviously. If-then-else and guards
-- are fine too as long as you pattern match the list.
--
-- Examples:
-- unrepeat [True,True,True,True] => [True]
-- unrepeat [1,1,2,1,3,3,3] => [1,2,1,3]
unrepeat :: Eq a => [a] -> [a]
unrepeat [] = []
unrepeat [a] = [a]
unrepeat (x0:x1:xs) = if x0 == x1
then unrepeat $ x1:xs
else x0:(unrepeat $ x1:xs)
------------------------------------------------------------------------------
-- Ex 5: Given a list of Either String Int, sum all the integers.
-- Return Nothing if no integers were present in the list.
--
-- Examples:
-- sumEithers [Left "fail", Left "xxx"] ==> Nothing
-- sumEithers [Left "fail", Right 1, Left "xxx", Right 2] ==> Just 3
sumEithers :: [Either String Int] -> Maybe Int
sumEithers xs = let filtered = filter (\x -> case x of Left _ -> False
Right x -> True) xs
vals = map (\x -> case x of Left _ -> 0
Right x -> x) filtered
in if length vals > 0 then Just $ sum vals else Nothing
------------------------------------------------------------------------------
-- Ex 6: Define the data structure Shape with values that can be
-- either circles or rectangles. A circle has just a radius, and a
-- rectangle has a width and a height.
--
-- Use _two_ constructors, one for circles, one for rectangles.
--
-- Implement the function area that computes the area of a Shape
--
-- Also implement the functions circle and rectangle that create
-- circles and rectangles (don't worry if this feels stupid, I need
-- these for the tests :)
--
-- All dimensions should be Doubles.
data Shape = Circle Double | Rectangle Double Double
deriving Show -- leave this line in place
circle :: Double -> Shape
circle r = Circle r
rectangle :: Double -> Double -> Shape
rectangle w h = Rectangle w h
area :: Shape -> Double
area (Circle r) = pi * (r ** 2)
area (Rectangle w h) = w * h
------------------------------------------------------------------------------
-- Ex 7: Here's a Card type for a deck of cards with just two suits
-- and a joker. Implement Eq and Ord instances for Card.
--
-- The Ord instance should order cards such that
-- - Cards of the same suit are ordered according to value
-- - Suits are ordered Heart > Spade
-- - Joker is the largest card
--
-- Examples:
-- Spade 1 == Spade 2 ==> False
-- sort [Heart 3, Spade 2, Joker, Heart 1] ==> [Spade 2,Heart 1,Heart 3,Joker]
data Card = Heart Int | Spade Int | Joker
deriving Show
instance Eq Card where
(Heart a) == (Heart b) = a == b
(Spade a) == (Spade b) = a == b
Joker == Joker = True
_ == _ = False
instance Ord Card where
compare (Heart a) (Heart b) = compare a b
compare (Spade a) (Spade b) = compare a b
compare (Spade _) (Heart _) = LT
compare _ Joker = LT
compare _ _ = GT
------------------------------------------------------------------------------
-- Ex 8: Here's a type Twos for things that always come in pairs. It's
-- like a list, but it has an even number of elements (and is also
-- never empty).
--
-- Implement a Functor instance for Twos.
data Twos a = End a a | Continue a a (Twos a)
deriving (Show, Eq)
instance Functor Twos where
fmap f (End a b) = End (f a) (f b)
fmap f (Continue a b c) = Continue (f a) (f b) (fmap f c)
------------------------------------------------------------------------------
-- Ex 9: Use the state monad to update the state with the sum of the
-- even numbers in a list. Do this by implementing the step function
-- below so that the sumEvens operation works correctly.
--
-- Examples:
-- execState (sumEvens [1,2,3,4]) 0
-- 6
step :: Int -> State Int ()
step i = modify $ \x -> if even i then x+i else x
sumEvens :: [Int] -> State Int ()
sumEvens is = forM_ is step
------------------------------------------------------------------------------
-- Ex 10: Here's a type Env for values that depend on an environment
-- (represented here by just a String). You'll also find some
-- utilities and example operations of type Env.
--
-- Your job is to define Functor and Monad instances for Env.
--
-- Examples of how the instances should work:
--
--
-- runEnv (fmap (+1) (return 3)) "env" ==> 4
-- runEnv (fmap (*2) envLength) "boing" ==> 10
-- runEnv (return 3) "env" ==> 3
-- runEnv (envLength >>= multiply) "xyz" ==> "xyzxyzxyz"
-- runEnv (greet >>= \g -> return ("The greeting is: "++g)) "bob"
-- ==> "The greeting is: Hello, bob"
--
-- Hint: consider W5 ex16
data Env a = MkEnv (String -> a)
runEnv :: Env a -> String -> a
runEnv (MkEnv f) str = f str
-- return a greeting for the name in the environment
greet :: Env String
greet = MkEnv (\name -> "Hello, "++name)
-- return the length of the environment
envLength :: Env Int
envLength = MkEnv (\name -> length name)
-- return a string consisting of n copies of the env
multiply :: Int -> Env String
multiply n = MkEnv (\name -> concat (replicate n name))
instance Functor Env where
fmap f (MkEnv a) = MkEnv (f . a)
instance Monad Env where
e >>= f = MkEnv b
where b str = let a = runEnv e str
e1 = f a
in runEnv e1 str
return x = MkEnv (\e -> x)
-- 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 Env where
pure = return
(<*>) = ap