Tricky folds like this one can often be solved by having the fold build up a function rather than try to build the result directly.
adjacents :: [a] -> [(a, a)]
adjacents xs = foldr f (const []) xs Nothing
where f curr g (Just prev) = (prev, curr) : g (Just curr)
f curr g Nothing = g (Just curr)
Here, the idea is to let the result be a function of type Maybe a -> [(a, a)] where the Maybe contains the previous element, or Nothing if we're at the beginning of the list.
Let's take a closer look at what's going on here:
If we have both a previous and a current element, we make a pair and pass the current element to the result of the recursion, which is the function which will generate the tail of the list.
f curr g (Just prev) = (prev, curr) : g (Just curr)
If there is no previous element, we just pass the current one to the next step.
f curr g Nothing = g (Just curr)
The base case const [] at the end of the list just ignores the previous element.
By doing it this way, the result is as lazy as your original definition:
> adjacents (1 : 2 : 3 : 4 : undefined)
[(1,2),(2,3),(3,4)*** Exception: Prelude.undefined
(x,y) : adjacents (y:xs)is better than[(x,y)] ++ adjacents (y:xs), and Marcin is right that zip is good:zip xs (tail xs)is clear and clean.mapandfilter) are elementary recursion schemes - they look at only one element at each step. As AndrewC shows, you have to "double" the input in order to see two elements at a step. There is a recursion scheme you can use to avoid doubling the input - paramorphism.Paratraverses the input like a fold but also lets you look at the "rest of input" at the recursive step. Unfortunatelyparaisn't in the Prelude or Data.List.