-
Notifications
You must be signed in to change notification settings - Fork 18
/
Copy pathW4.hs
320 lines (280 loc) · 9.47 KB
/
W4.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
module W4 where
import Control.Monad
import Data.List
import Data.IORef
import System.IO
-- Week 4:
-- * The IO type
-- * do-notation
--
-- Useful functions / operations:
-- * putStrLn
-- * getLine
-- * readLn
-- * replicateM
-- * readFile
-- * lines
--
-- NB! Do not add any additional imports!
--
-- NB! Do not use IORef in any exercise except 15!
------------------------------------------------------------------------------
-- Ex 1: define an IO operation hello that prints two lines. The
-- first line should be HELLO and the second one WORLD
hello :: IO ()
hello = undefined
------------------------------------------------------------------------------
-- Ex 2: define the IO operation greet that takes a name as an
-- argument and prints a line "HELLO name".
greet :: String -> IO ()
greet name = undefined
------------------------------------------------------------------------------
-- Ex 3: define the IO operation greet2 that reads a name from the
-- keyboard and then greets that name like the in the previous
-- exercise.
--
-- Try to use the greet operation in your solution.
greet2 :: IO ()
greet2 = undefined
------------------------------------------------------------------------------
-- Ex 4: define the IO operation getSum that reads two numbers, on
-- separate lines, from the user, and produces their sum.
--
-- Remember the operation readLn.
getSum :: IO Int
getSum = undefined
------------------------------------------------------------------------------
-- Ex 5: define the IO operation readWords n which reads n lines from
-- the user and returns them in alphabetical order.
readWords :: Int -> IO [String]
readWords n = undefined
------------------------------------------------------------------------------
-- Ex 6: define the IO operation readUntil f, which reads lines from
-- the user and returns them as a list. Reading is stopped when f
-- returns True for a line. (The value for which f returns True is not
-- returned.)
--
-- You'll probably need to make readUntil recursive (or use a
-- recursive helper operation).
readUntil :: (String -> Bool) -> IO [String]
readUntil f = undefined
------------------------------------------------------------------------------
-- Ex 7: isums n should read n numbers from the user and return their
-- sum. Additionally, after each read number, the sum up to that
-- number should be printed.
--
-- Reminder: do not use IORef
isums :: Int -> IO Int
isums n = undefined
------------------------------------------------------------------------------
-- Ex 8: when is a useful function, but its first argument has type
-- Bool. Write a function that behaves similarly but the first
-- argument has type IO Bool.
whenM :: IO Bool -> IO () -> IO ()
whenM cond op = undefined
------------------------------------------------------------------------------
-- Ex 9: implement the while loop. while condition operation should
-- run operation as long as condition returns True.
--
-- Examples:
-- while (return False) (putStrLn "IMPOSSIBLE") -- prints nothing
--
-- let ask :: IO Bool
-- ask = do putStrLn "Y/N?"
-- line <- getLine
-- return $ line == "Y"
-- in while ask (putStrLn "YAY!")
--
-- This prints YAY! as long as the user keeps answering Y
while :: IO Bool -> IO () -> IO ()
while cond op = undefined
------------------------------------------------------------------------------
-- Ex 10: given a string and an IO operation, print the string, run
-- the IO operation, print the string again, and finally return what
-- the operation returned.
--
-- Note! the operation should be run only once
--
-- Examples:
-- debug "CIAO" (return 3)
-- - prints two lines that contain CIAO
-- - returns the value 3
-- debug "BOOM" getLine
-- 1. prints "BOOM"
-- 2. reads a line from the user
-- 3. prints "BOOM"
-- 4. returns the line read from the user
debug :: String -> IO a -> IO a
debug s op = undefined
------------------------------------------------------------------------------
-- Ex 11: Reimplement mapM_ (specialized to the IO type) using
-- recursion and pattern matching.
--
-- In case you don't know what mapM_ does, it takes a parameterized IO
-- operation and a list of parameters, and runs the operation for each
-- value in the list.
--
-- Remember to use `return ()` so that you get the type right!
mymapM_ :: (a -> IO b) -> [a] -> IO ()
mymapM_ = undefined
------------------------------------------------------------------------------
-- Ex 12: Reimplement the function forM using pattern matching and
-- recursion.
myforM :: [a] -> (a -> IO b) -> IO [b]
myforM as f = undefined
------------------------------------------------------------------------------
-- Ex 13: sometimes one bumps into IO operations that return IO
-- operations. For instance the type IO (IO Int) means an IO operation
-- that returns an IO operation that returns an Int.
--
-- Implement the function doubleCall which takes an operation op and
-- 1. runs op
-- 2. runs the operation returned by op
-- 3. returns the value returned by this operation
--
-- Examples:
-- - doubleCall (return (return 3)) is the same as return 3
--
-- - let op :: IO (IO [String])
-- op = do l <- readLn
-- return $ replicateM l getLine
-- in doubleCall op
--
-- works just like
--
-- do l <- readLn
-- replicateM l getLine
doubleCall :: IO (IO a) -> IO a
doubleCall op = undefined
------------------------------------------------------------------------------
-- Ex 14: implement the analogue of function composition (the (.)
-- operator) for IO operations. That is, take an operation op1 of type
-- a -> IO b
-- an operation op2 of type
-- c -> IO a
-- and a value of type
-- c
-- and returns an operation op3 of type
-- IO b
--
-- op3 should of course
-- 1. take the value of type c and pass it to op2
-- 2. take the resulting value (of type a) and pass it to op1
-- 3. return the result (of type b)
compose :: (a -> IO b) -> (c -> IO a) -> c -> IO b
compose op1 op2 c = undefined
------------------------------------------------------------------------------
-- Ex 15: This exercises is about IORefs and operations that return
-- operations.
--
-- Implement the function mkCounter that returns the io operations
-- inc :: IO () and get :: IO Int. These operations should work like this:
--
-- get returns the number of times inc has been called
--
-- In other words, a simple stateful counter.
--
-- An example of how mkCounter works in GHCi:
--
-- *W4> (inc,get) <- mkCounter
-- *W4> inc
-- *W4> inc
-- *W4> get
-- 2
-- *W4> inc
-- *W4> inc
-- *W4> get
-- 4
mkCounter :: IO (IO (), IO Int)
mkCounter = undefined
------------------------------------------------------------------------------
-- Ex 16: fetch from the given file (Handle) the lines with the given
-- indices. Line indexing starts from 1. You can assume that the
-- numbers are given in ascending order.
--
-- Have a look at the docs for the System.IO module for help.
hFetchLines :: Handle -> [Int] -> IO [String]
hFetchLines h nums = undefined
------------------------------------------------------------------------------
-- Ex 17: CSV is a file format that stores a two-dimensional array of
-- values in a file. Each row of the file is a row of the array. Each
-- row of the file consists of values on that row separated with the ,
-- character.
--
-- Implement the function readCSV that reads a CSV file and returns it
-- as a list of lists.
--
-- NB! You don't need to handle the intricacies of real CSV, e.g.
-- quoting. You can assume each , character starts a new field.
--
-- NB! The lines might have different numbers of elements.
readCSV :: FilePath -> IO [[String]]
readCSV path = undefined
------------------------------------------------------------------------------
-- Ex 18: your task is to compare two files, a and b. The files should
-- have the same contents, but if lines at index i differ from each
-- other, you should print
--
-- < file a version of the line
-- > file b version of the line
--
-- Example:
--
-- File a contents:
-- a
-- aa
-- x
-- aa
-- bb
-- cc
--
-- File b contents:
-- a
-- aa
-- bb
-- aa
-- cc
-- dd
--
-- Output:
-- < x
-- > bb
-- < bb
-- > cc
-- < cc
-- > dd
--
-- NB! You can assume the files have the same number of rows.
--
-- Hint! It's probably wise to implement a pure function for finding
-- the differing lines. A suitable type could be
-- [String] -> [String] -> [String].
compareFiles :: FilePath -> FilePath -> IO ()
compareFiles a b = undefined
------------------------------------------------------------------------------
-- Ex 19: In this exercise we see how a program can be split into a
-- pure part that does all of the work, and a simple IO wrapper that
-- drives the pure logic.
--
-- Implement the function interact' that takes a pure function f of
-- type
-- (String, st) -> (Bool, String, st)
-- and a starting state of type st and returns an IO operation of type
-- IO st
--
-- interact' should read a line from the user, feed the line and the
-- current state to f. f then returns a boolean, a string to print and
-- a new state. The string is printed, and if the boolean is True, we
-- continue running with the new state. If the boolean is False, the
-- execution has ended and the state should be returned.
--
-- Example:
--
-- let f :: (String,Integer) -> (Bool,String,Integer)
-- f ("inc",n) = (True,"",n+1)
-- f ("print",n) = (True,show n,n)
-- f ("quit",n) = (False,"bye bye",n)
-- in interact' f 0
--
interact' :: ((String,st) -> (Bool,String,st)) -> st -> IO st
interact' f state = undefined