-
Notifications
You must be signed in to change notification settings - Fork 6
/
Foldr.hs
273 lines (201 loc) · 5.95 KB
/
Foldr.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
{-
---
fulltitle: "In class exercise: foldr"
date: September 11, 2024
---
In [HigherOrder](HigherOrder.html) we saw a few functions that you could write using the
general purpose `foldr` function. This function captures the general pattern of
list recursion and is also good practice for working with higher-order functions.
This set of exercises is intended to give you more practice with using `foldr`
on lists. You should start with one at the right level for you and your
partner (they are ordered in terms of difficulty).
If you finish this module, you should also look at variations in the definition
of `foldr` shown in the module [Sum](Sum.html).
-}
module Foldr where
-- See: https://www.seas.upenn.edu/~cis5520/current/lectures/stub/02-higherorder/Foldr.html
import Prelude hiding (all, filter, foldl, foldl1, last, length, map, reverse)
{-
Length Example
--------------
This function counts the number of elements stored in a list. The recursive
definition of the `length` function is
-}
-- >>> length1 "abc"
-- 3
length1 :: [a] -> Int
length1 [] = 0
length1 (_ : xs) = 1 + length1 xs
{-
Now we can rewrite it in terms of foldr.
-}
length :: [a] -> Int
length = foldr (\_ n -> 1 + n) 0
{-
and test it on some inputs
-}
-- >>> length "abc"
-- 3
-- >>> length ""
-- 0
{-
Once we have completed the foldr version, we can trace through its evaluation using the
argument `['a','b','c']`.
The evaluation starts out as:
foldr (\_ n -> 1 + n) 0 ['a', 'b', 'c']
= (_ n -> 1 + n) 'a' (foldr (\_ n -> 1 + n) 0 ['b', 'c'])
= 1 + (foldr (\_ n -> 1 + n) 0 ['b', 'c'])
By unfolding the definition of foldr one step as above we can kind of see what
it's doing. It's just adding 1 each time we encounter an element. The first
argument in the anonymous function is just ignored, because you don't care
what the elements of the list are when you're just counting them. The n is the
accumulator, representing the rest of the fold (which in this case is the
length of the tail). 1 + length of tail gives you the length of your list.
Unfolding this further we can see how the whole thing would evaluate:
foldr (\_ n -> 1 + n) 0 ['a', 'b', 'c']
= 1 + (foldr (\_ n -> 1 + n) 0 ['b', 'c'])
= 1 + (1 + (foldr (\_ n -> 1 + n) 0 ['c']))
-- Skipping to when the anonymous function is applied :)
= 1 + (1 + (1 + (foldr (\_ n -> 1 + n) 0 [])))
= 1 + (1 + (1 + 0))
= 1 + (1 + 1)
= 1 + 2
= 3
Note, this evaluation can also be generated by the [online tool](http://bm380.user.srcf.net/cgi-bin/stepeval.cgi?expr=foldr+%28%5C_+n+-%3E+1+%2B+n%29+0+%5B%27a%27%2C+%27b%27%2C+%27c%27%5D).
Feel free to use this tool below, but understand that it is fairly
simplistic. It won't be able to handle all of the examples that you throw at
it.
All
---
Calculate whether *all* elements of a list satisfy a given predicate. The recursive definition is
-}
all1 :: (a -> Bool) -> [a] -> Bool
all1 _ [] = True
all1 p (x : xs) = p x && all1 p xs
{-
Now implement using foldr
-}
-- >>> all (>10) ([1 .. 20] :: [Int])
-- False
-- >>> all (>0) ([1 .. 20] :: [Int])
-- True
all :: (a -> Bool) -> [a] -> Bool
all p = undefined
{-
And trace through an evaluation `all not [True,False]`:
(Also available from [online tool](http://bm380.user.srcf.net/cgi-bin/stepeval.cgi?expr=let+all+p+%3D+foldr+%28%5Cx+y+-%3E+p+x+%26%26+y%29+True+in%0D%0Aall+not+%5BTrue%2CFalse%5D+%0D%0A++%0D%0A).)
Last
----
Find and return the last element of the lists (if the list is nonempty).
The recursive definition is
-}
last1 :: [a] -> Maybe a
last1 [] = Nothing
last1 (x : xs) = case xs of
[] -> Just x
_ -> last1 xs
{-
Now implement using foldr
-}
-- >>> last "abcd"
-- Just 'd'
-- >>> last ""
-- Nothing
last :: [a] -> Maybe a
last = undefined
{-
and trace through the evaluation `last [1,2]`
Filter
-----
The filter function selects items from a list that satisfy a given
predicate. The output list should contain only the elements
of the first list for which the input function returns `True`.
-}
filter :: (a -> Bool) -> [a] -> [a]
filter p = undefined
-- >>> filter (> 10) [1 .. 20]
-- >>> filter (\l -> sum l <= 42) [10,20], [50,50], [1..5] ]
{-
Trace the evaluation of `filter (>2) [2,3]`
-}
{-
Reverse
-------
Reverse the elements appearing in the list.
Consider this linear time version that used direct recursion.
-}
reverse1 :: [a] -> [a]
reverse1 l = aux l []
where
aux [] = id
aux (x : xs) = \ys -> aux xs (x : ys)
{-
Now rewrite this function using 'foldr'
-}
reverse :: [a] -> [a]
reverse l = undefined
-- >>> reverse "abcd"
-- "dcba"
-- >>> reverse ""
-- ""
{-
And trace through its evaluation on the list `['a','b','c']`:
-}
{-
Intersperse
-----------
The intersperse function takes an element and a list
and `intersperses' that element between the elements of the list.
For example,
-}
-- >>> intersperse ',' "abcde"
-- "a,b,c,d,e"
{-
The recursive version looks like this:
-}
intersperse1 :: a -> [a] -> [a]
intersperse1 _ [] = []
intersperse1 a (x : xs) = case xs of
[] -> [x]
_ -> x : a : intersperse1 a xs
{-
Now rewrite using 'foldr'
-}
intersperse :: a -> [a] -> [a]
intersperse = undefined
-- >>> intersperse ',' "abcde"
-- "a,b,c,d,e"
-- >>> intersperse ',' ""
-- ""
{-
and trace through an example of `intersperse ',' "ab"`
-}
{-
foldl
-----
Here is the usual recursive definition for "fold left".
-}
foldl1 :: (b -> a -> b) -> b -> [a] -> b
foldl1 _ z [] = z
foldl1 f z (x : xs) = foldl1 f (z `f` x) xs
{-
You can see that `foldl1` is different than `foldr` by comparing the results on various examples:
-}
-- >>> foldl1 (flip (:)) [] [1,2,3]
-- [3,2,1]
-- >>> foldr (:) [] [1,2,3]
-- [1,2,3]
-- >>> foldl1 (++) "x" ["1","2","3"]
-- "x123"
-- >>> foldr (++) "x" ["1","2","3"]
-- "123x"
{-
But, you can also define `foldl` in terms of `foldr`. Give it a try.
-}
foldl :: (b -> a -> b) -> b -> [a] -> b
foldl f z xs = undefined
-- >>> foldl (++) "x" ["1", "2", "3"]
-- "x123"
{-
And trace through the test case above.
-}