A pattern to unfold a list
The following function encapsulates the pattern to produce a list from a single value:
-- unfolding a list
unfold :: (a -> Bool) -> (a -> b) -> (a -> a) -> a -> [b]
unfold p h t x | p x = []
| otherwise = h x : unfold p h t (t x)
That is, if the predicate, p
, is true for the input argument then an empty is produced. Otherwise, h
is applied to the argument to produce the head of the list and is consed to the result of recursively applying unfold
to a value produced by applying the function t
to the argument.
Chop
Let's say we want to create a list of lists by chopping up a single list into chunks of same sizes:
chop4 :: [a] -> [[a]]
chop4 = unfold null (take 4) (drop 4)
We stop when the input list is empty. Head is produced by taking 4 elements from the input list. Next input is produced by dropping 4 elements from the input list.
*Main> chop4 [1..12]
[[1,2,3,4],[5,6,7,8],[9,10,11,12]]
Map
Even map can be defined using unfold:
map :: (a -> b) -> [a] -> [b]
map f = unfold null (f . head) tail
We stop when the input list is empty. Head is produced by applying f
to head
of input list and next input is produced by applying tail
to the current input.
Iterate
iterate
is a library function that produces a list from a single value by repeatedly applying a function to that value. Since the function is repeatedly applied, its type is constrained to (a -> a)
. Following is an interesting implementation of iterate
using unfold
:
iter :: (a -> a) -> a -> [a]
iter f = unfold (const False) id f
Since iterate
produces an infinite list, we use const False
to always produce False
for predicate. Since the list starts with the given input value, we use id
function for head. f
is then used to produce the next input.
*Main> take 10 (iter (+1) 0)
[0,1,2,3,4,5,6,7,8,9]