How can this function be written using foldr?

放肆的年华 提交于 2020-01-14 14:07:41

问题


I have this simple function which returns a list of pairs with the adjacents elements of a list.

adjacents :: [a] -> [(a,a)]
adjacents (x:y:xs) = [(x,y)] ++ adjacents (y:xs)
adjacents (x:xs) = []

I'm having problems trying to write adjacents using foldr. I've done some research but nothing seems to give me a hint. How can it be done?


回答1:


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:

  1. 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)
    
  2. If there is no previous element, we just pass the current one to the next step.

    f curr g Nothing     = g (Just curr)
    
  3. 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



回答2:


I don't think your function is a good fit for a fold, because it looks at two elements rather than one.

I think the best solution to the problem is

adjacents [] = []
adjacents xs = zip xs (tail xs)

But we can shoehorn it into a travesty of a fold if you like. First an auxilliary function.

prependPair :: a -> [(a,a)] -> [(a,a)]
prependPair x [] = [(x,b)]             where b = error "I don't need this value."
prependPair x ((y,z):ys) = ((x,y):(y,z):ys)

adjacents' xs = init $ foldr prependPair [] xs

I feel like I've cheated slightly by making and throwing away the last element with the error value, but hey ho, I already said I don't think foldr is a good way of doing this, so I guess this hack is an example of it not being a fold.




回答3:


You can also try unfoldr instead of foldr.

import Data.List
adjacents xs = unfoldr f xs where
  f (x:rest@(y:_)) = Just ((x,y), rest)
  f _ = Nothing


来源:https://stackoverflow.com/questions/13057028/how-can-this-function-be-written-using-foldr

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!