-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathChapter18.lhs
More file actions
363 lines (237 loc) · 7.33 KB
/
Copy pathChapter18.lhs
File metadata and controls
363 lines (237 loc) · 7.33 KB
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
Haskell: The Craft of Functional Programming
Simon Thompson
(c) Addison-Wesley, 1999.
Chapter 18
Programming with actions
^^^^^^^^^^^^^^^^^^^^^^^^
> module Chapter18 where
> import Prelude hiding (lookup)
> import IO -- for isEOF (see note below, aslo)
> isEOF = hugsIsEOF -- this should be commented out in later
> -- versions; it is here because Hugs 1.4
> -- doesn't support isEOF
The basics of input/output
^^^^^^^^^^^^^^^^^^^^^^^^^^
Reading input is done by getLine and getChar: see Prelude for details.
getLine :: IO String
getChar :: IO Char
Text strings are written using
putStr :: String -> IO ()
putStrLn :: String -> IO ()
A hello, world program
> helloWorld :: IO ()
> helloWorld = putStr "Hello, World!"
Writing values in general
print :: Show a => a -> IO ()
The do notation: a series of sequencing examples.
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Put a string and newline.
putStrLn :: String -> IO ()
putStrLn str = do putStr str
putStr "\n"
Put four times.
> put4times :: String -> IO ()
> put4times str
> = do putStrLn str
> putStrLn str
> putStrLn str
> putStrLn str
Put n times
> putNtimes :: Int -> String -> IO ()
> putNtimes n str
> = if n <= 1
> then putStrLn str
> else do putStrLn str
> putNtimes (n-1) str
Read two lines, then write a message.
> read2lines :: IO ()
> read2lines
> = do getLine
> getLine
> putStrLn "Two lines read."
Read then write.
> getNput :: IO ()
> getNput = do line <- getLine
> putStrLn line
Read, process then write.
> reverse2lines :: IO ()
> reverse2lines
> = do line1 <- getLine
> line2 <- getLine
> putStrLn (reverse line2)
> putStrLn (reverse line1)
Last example redefined to use a local definition.
> reverse2lines' :: IO ()
> reverse2lines'
> = do line1 <- getLine
> line2 <- getLine
> let rev1 = reverse line1
> let rev2 = reverse line2
> putStrLn rev2
> putStrLn rev1
Reading an Int.
> getInt :: IO Int
> getInt = do line <- getLine
> return (read line :: Int)
Iteration and recursion
^^^^^^^^^^^^^^^^^^^^^^^
A while loop.
> while :: IO Bool -> IO () -> IO ()
> while test action
> = do res <- test
> if res then do action
> while test action
> else return ()
Copying input to output.
> copyInputToOutput :: IO ()
> copyInputToOutput
> = while (do res <- isEOF
> return (not res))
> (do line <- getLine
> putStrLn line)
An important example: refer to the text to see why it fails to work as
required. (The incorrect version is primed.)
> goUntilEmpty' :: IO ()
> goUntilEmpty'
> = do line <- getLine
> while (return (line /= []))
> (do putStrLn line
> line <- getLine
> return ())
The correct program: the key is to think recursively.
> goUntilEmpty :: IO ()
> goUntilEmpty
> = do line <- getLine
> if (line == [])
> then return ()
> else (do putStrLn line
> goUntilEmpty)
Adding a sequence of integers
> sumInts :: IO Int
> sumInts
> = do n <- getInt
> if n==0
> then return 0
> else (do m <- sumInts
> return (n+m))
Addiing a sequence of integers, courteously.
> sumInteract :: IO ()
> sumInteract
> = do putStrLn "Enter integers one per line"
> putStrLn "These will be summed until zero is entered"
> sum <- sumInts
> putStr "The sum was "
> print sum
The calculator
^^^^^^^^^^^^^^
This is available separately.
Input and output as lazy lists
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Reverse all the lines in the input.
> listIOprog :: String -> String
> listIOprog = unlines . map reverse . lines
Monads for Functional Programming
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The definition of the Monad class
class Monad m where
(>>=) :: m a -> (a -> m b) -> m b
return :: a -> m a
fail :: String -> m a
Kelisli composition for monadic functions.
(>@>) :: Monad m => (a -> m b) ->
(b -> m c) ->
(a -> m c)
f >@> g = \ x -> (f x) >>= g
Some examples of monads
^^^^^^^^^^^^^^^^^^^^^^^
Some examples from the standard prelude.
The list monad
instance Monad [] where
xs >>= f = concat (map f xs)
return x = [x]
zero = []
The Maybe monad
instance Monad Maybe where
(Just x) >>= k = k x
Nothing >>= k = Nothing
return = Just
The identity monad
> data Id a = Id a
> instance Monad Id where
> return = Id
> (>>=) (Id x) f = f x
The parsing monad
data SParse a b = SParse (Parse a b)
instance Monad (SParse a) where
return x = SParse (succeed x)
zero = SParse fail
(SParse pr) >>= f
= SParse (\s -> concat [ sparse (f x) rest | (x,rest) <- pr st ])
sparse :: SParse a b -> Parse a b
sparse (SParse pr) = pr
A state monad (the state need not be a table; this example is designed
to support the example discussed below.)
> type Table a = [a]
> data State a b = State (Table a -> (Table a , b))
> instance Monad (State a) where
> return x = State (\tab -> (tab,x))
> (State st) >>= f
> = State (\tab -> let
> (newTab,y) = st tab
> (State trans) = f y
> in
> trans newTab)
Example: Monadic computation over trees
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
A type of binary trees.
> data Tree a = Nil | Node a (Tree a) (Tree a)
Summing a tree of integers
A direct solution:
> sTree :: Tree Int -> Int
> sTree Nil = 0
> sTree (Node n t1 t2) = n + sTree t1 + sTree t2
A monadic solution: first giving a value of type Id Int ...
> sumTree :: Tree Int -> Id Int
> sumTree Nil = return 0
> sumTree (Node n t1 t2)
> = do num <- return n
> s1 <- sumTree t1
> s2 <- sumTree t2
> return (num + s1 + s2)
... then adapted to give an Int solution
> sTree' :: Tree Int -> Int
> sTree' = extract . sumTree
where the value is extracted from the Id monad thus:
> extract :: Id a -> a
> extract (Id x) = x
Using a state monad in a tree calculation
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The top level function ...
> numTree :: Eq a => Tree a -> Tree Int
... and the function which does all the work:
> numberTree :: Eq a => Tree a -> State a (Tree Int)
Its structure mirrors exactly the structure of the earlier program to
sum the tree.
> numberTree Nil = return Nil
> numberTree (Node x t1 t2)
> = do num <- numberNode x
> nt1 <- numberTree t1
> nt2 <- numberTree t2
> return (Node num nt1 nt2)
The work of the algorithm is done node by node, hence the function
> numberNode :: Eq a => a -> State a Int
> numberNode x = State (nNode x)
> nNode :: Eq a => a -> (Table a -> (Table a , Int))
> nNode x table
> | elem x table = (table , lookup x table)
> | otherwise = (table++[x] , length table)
Looking up a value in the table; will side-effect the table if the value
is not present.
> lookup :: Eq a => a -> Table a -> Int
> lookup = lookup -- dummy definition:
> -- exercise for the reader
Extracting a value froma state monad.
> extractSt :: State a b -> b
> extractSt (State st) = snd (st [])
The top-level function defined eventually.
> numTree = extractSt . numberTree