-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSet11b.hs
187 lines (169 loc) · 5.7 KB
/
Set11b.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
module Set11b where
import Control.Monad
import Data.List
import Data.IORef
import System.IO
import Mooc.Todo
------------------------------------------------------------------------------
-- Ex 1: Given an IORef String and a list of Strings, update the value
-- in the IORef by appending to it all the strings in the list, in
-- order.
--
-- Example:
-- *Set11b> r <- newIORef "x"
-- *Set11b> appendAll r ["foo","bar","quux"]
-- *Set11b> readIORef r
-- "xfoobarquux"
appendAll :: IORef String -> [String] -> IO ()
appendAll ref strings = modifyIORef ref (++ concat strings)
------------------------------------------------------------------------------
-- Ex 2: Given two IORefs, swap the values stored in them.
--
-- Example:
-- *Set11b> x <- newIORef "x"
-- *Set11b> y <- newIORef "y"
-- *Set11b> swapIORefs x y
-- *Set11b> readIORef x
-- "y"
-- *Set11b> readIORef y
-- "x"
swapIORefs :: IORef a -> IORef a -> IO ()
swapIORefs ref1 ref2 = do
val1 <- readIORef ref1
val2 <- readIORef ref2
writeIORef ref1 val2
writeIORef ref2 val1
------------------------------------------------------------------------------
-- Ex 3: 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 = do
innerOp <- op
innerOp
------------------------------------------------------------------------------
-- Ex 4: 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 = do
a <- op2 c
op1 a
------------------------------------------------------------------------------
-- Ex 5: Reading lines from a file. The module System.IO defines
-- operations for Handles, which represent open files that can be read
-- from or written to. Here are some functions that might be useful:
--
-- * hGetLine :: Handle -> IO String
-- Reads one line from the Handle. Will fail if the Handle is at the
-- end of the file
-- * hIsEOF :: Handle -> IO Bool
-- Produces True if the Handle is at the end of the file.
-- * hGetContents :: Handle -> IO String
-- Reads content from Handle until the end of the file.
--
-- Implement the function hFetchLines which returns the contents of
-- the given handle as a sequence of lines.
--
-- There are multiple ways to implement this function. You can either
-- read the lines one by one, or read the whole file and then worry
-- about splitting lines. Both approaches are fine, and you can even
-- try out both!
--
-- Example:
-- *Set11b> h <- openFile "Set11b.hs" ReadMode
-- *Set11b> ls <- hFetchLines h
-- *Set11b> take 3 ls
-- ["module Set11b where","","import Control.Monad"]
hFetchLines :: Handle -> IO [String]
hFetchLines h = do
contents <- hGetContents h
return (lines contents)
------------------------------------------------------------------------------
-- Ex 6: Given a Handle and a list of line indexes, produce the lines
-- at those indexes from the file.
--
-- Line indexing starts from 1.
--
-- Here too, there are multiple ways to implement this. You can try
-- using hFetchLines, or writing out a loop that gets lines from the
-- handle.
hSelectLines :: Handle -> [Int] -> IO [String]
hSelectLines h nums = do
lines <- hFetchLines h
return [lines !! (n-1) | n <- nums, n > 0, n <= length lines]
------------------------------------------------------------------------------
-- Ex 7: 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:
-- *Set11b> interact' counter 1
-- print
-- 1
-- inc
-- done
-- inc
-- done
-- print
-- 3
-- quit
-- bye bye
-- 3
-- *Set11b>
-- This is used in the example above. Don't change it!
counter :: (String,Integer) -> (Bool,String,Integer)
counter ("inc",n) = (True,"done",n+1)
counter ("print",n) = (True,show n,n)
counter ("quit",n) = (False,"bye bye",n)
interact' :: ((String, st) -> (Bool, String, st)) -> st -> IO st
interact' f state = do
line <- getLine
case f (line, state) of
(True, output, newState) -> do
putStrLn output
interact' f newState
(False, output, newState) -> do
putStrLn output
return newState