Reputation: 48592
Consider one of these implementations of a function to remove consecutive duplicates from a list:
uniq :: Eq a => [a] -> [a]
uniq [] = []
uniq (x:xs) = x:uniq (dropWhile (x ==) xs)
uniq :: Eq a => [a] -> [a]
uniq (x:xs@(y:_))
| x == y = x:tail (uniq xs)
| otherwise = x:uniq xs
uniq l = l
They both work as expected on both finite and infinite lists. To be more specific, for infinite lists, I expect take n $ uniq l
to terminate as long as there are no infinitely long sequences of duplicate values before there are n
values to return.
Now consider this attempt at such a function, using foldr
:
uniq :: (Foldable t, Eq a) => t a -> [a]
uniq = foldr uniqHelper []
where uniqHelper x [] = [x]
uniqHelper x acc@(y:_)
| x == y = acc
| otherwise = x:acc
This works correctly on finite lists, but never terminates for infinite ones, because uniqHelper
always needs to evaluate its second argument. Is this something that can be fixed with a more clever uniqHelper
, or is it inherently impossible to use folding for this task?
Upvotes: 4
Views: 492
Reputation: 71075
Your first snippet produces the first x out of many but the third snippet produces the last x, that's the reason for the discrepancy.
To faithfully render the first snippet as a right fold, we fold into functions so we can pass a state argument along, occasionally updated to the new unique element of the list:
uniq [] = []
uniq (x:xs) = x : foldr g (const []) xs x
where
g y r x | y==x = r x
| otherwise = y : r y
This actually skips the duplicates instead of re-consing and then ignoring them over and over as the other two answers are doing, which are actually equivalent to each other: the dropWhile
could only ever skip over no more than one element, which will then be skipped by the following reducer calling its dropWhile (==x)
.
I always use r
as the name of the reducer's second argument, as a mnemonic device for "recursive result". Here the presence of yet another argument after r
in g
's definition means that r
is a function, which we can pass an updated value serving as a state.
This technique enables using foldr
to encode stateful computations like take
, drop
, dropWhile
, takeWhile
, etc..
> head $ uniq $ repeat 1 1 > take 10 $ uniq [n | n <- [1..], n <- replicate n n] [1,2,3,4,5,6,7,8,9,10] > uniq [1..10] [1,2,3,4,5,6,7,8,9,10]
Upvotes: 1
Reputation: 7138
You can just combine your implementations:
uniq :: Eq a => [a] -> [a]
uniq = foldr uniqHelper []
where uniqHelper x acc = x : dropWhile (==x) acc
to get desired behaviour on infinite lists:
Prelude> take 5 $ uniq [1..]
[1,2,3,4,5]
Upvotes: 1
Reputation: 476729
You can transfer the removal of an element to the tail, so regardless of the value, we first "yield" x
, and then we use a function (here tl
) to evaluate the tail of the list:
uniq :: (Foldable t, Eq a) => t a -> [a]
uniq = foldr uniqHelper []
where uniqHelper x acc = x : tl acc
where tl acc@(x2:xs) | x == x2 = xs
| otherwise = acc
tl [] = []
We thus first yield x
, and then worry about the next element later. If second parameter (the folded list of the tail of the list) contains the same element, then we "remove" it, otherwise, we retain the entire list.
The above is also able to yield the first element of a repeat
, for example:
Prelude> head (uniq (repeat 1))
1
Prelude> take 5 $ uniq [1..]
[1,2,3,4,5]
Prelude> uniq [1,1,1,1,2,2,2,1,1,1,1,1,1,3,3,3,3,1,1]
[1,2,1,3,1]
Of course if there are an infinite amount of 0
s followed by a 1
, that 1
is never emitted:
Prelude> uniq (repeat 0 ++ [1])
[0
Upvotes: 3