undefined
.
Eventually, the complete
version will be made available.
In class exercise: foldr
In Lec3 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' with lists. You should start with one at the right level for you and your partner (they are ordered in terms of difficulty). You won't have time in class to do them all.
The solutions will be posted after class. Feel free to discuss on Piazza if you would like clarification.
Length Example
This function counts the number of elements stored in a list. The recursive definition of the length function is
Now we can rewrite it in terms of foldr.
and test it on some inputs
Once we have completed the foldr version, we can trace through its evaluation using the argument ['a','b','c']
. (This explanation is based on a Piazza post.)
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.
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 pred [] = True
> all1 pred (x:xs) = pred x && all1 pred xs
Now implement using foldr
> testAll :: Test
> testAll = "all" ~: TestList [
> all (>10) ([1 .. 20] :: [Int]) ~?= False,
> all (>0) ([1 .. 20] :: [Int]) ~?= True
> ]
And trace through an evaluation all not [True,False]
:
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 :: [a] -> Maybe a
> last = undefined
>
> testLast = "last" ~: TestList [
> last "abcd" ~?= Just 'd'
> , last "" ~?= Nothing
> ]
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
.
> testFilter :: Test
> testFilter = "filter" ~: TestList [
> filter (>10) [1..20] ~?= [11..20],
> filter (\l -> sum l <= 42) [ [10,20], [50,50], [1..5] ] ~?= [[10,20],[1..5]] ]
Try this on 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'
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,
The recursive version looks like this:
> intersperse1 :: a -> [a] -> [a]
> intersperse1 a [] = []
> intersperse1 a (x:xs) = case xs of
> [] -> [x]
> _ -> x : a : intersperse1 a xs
Now rewrite using 'foldr'
> testIntersperse :: Test
> testIntersperse = "intersperse" ~:
> TestList [ "intersperse0" ~: intersperse ',' "abcde" ~=? "a,b,c,d,e",
> "intersperse1" ~: intersperse 0 [] ~=? [] ]
and trace through an example of intersperse ',' "ab"
foldl
Here is the usual recursive definition:
> foldl1 :: (b -> a -> b) -> b -> [a] -> b
> foldl1 f z [] = z
> foldl1 f z (x:xs) = let z' = z `f` x
> in foldl1 f z' xs
And, here is how to write foldl in terms of foldr.
Reimplement this function using 'foldr'.
And trace through the test case above.
NOTE: the in class version of this exercise had the wrong version of foldl. Here is the one that we saw in class.
> foldl1_wrong :: (b -> a -> b) -> b -> [a] -> b
> foldl1_wrong f b [] = b
> foldl1_wrong f b (x : xs) = f (foldl1_wrong f b xs) x
This version has an easy implementation in terms of foldr.