如何使用foldr编写此函数?

时间:2012-10-24 20:18:16

标签: haskell recursion functional-programming fold higher-order-functions

我有这个简单的函数,它返回一个带有列表的邻接元素的对列表。

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

我在尝试使用foldr编写邻接时遇到问题。我做了一些研究,但似乎没有什么能给我一个暗示。怎么办呢?

3 个答案:

答案 0 :(得分:6)

像这样的棘手折叠通常可以通过折叠构建函数而不是尝试直接构建结果来解决。

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)

这里的想法是让结果为Maybe a -> [(a, a)]类型的函数,其中Maybe包含前一个元素,如果我们位于列表的开头,则为Nothing

让我们仔细看看这里发生了什么:

  1. 如果我们同时有一个previous元素和一个current元素,我们建立一个对并将当前元素传递给递归的结果,该函数将生成列表的尾部。

    f curr g (Just prev) = (prev, curr) : g (Just curr)
    
  2. 如果没有上一个元素,我们只需将当前元素传递给下一步。

    f curr g Nothing     = g (Just curr)
    
  3. 列表末尾的基本案例const []会忽略前一个元素。

  4. 通过这种方式,结果与原始定义一样懒惰:

    > adjacents (1 : 2 : 3 : 4 : undefined)
    [(1,2),(2,3),(3,4)*** Exception: Prelude.undefined
    

答案 1 :(得分:2)

我认为你的功能不适合折叠,因为它看两个元素而不是一个。

我认为问题的最佳解决方案是

adjacents [] = []
adjacents xs = zip xs (tail xs)
但是如果你愿意,我们可以把它变成一个折叠的嘲弄。首先是辅助功能。

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

我觉得自己因制作和投掷而略有欺骗 离开最后一个带有错误值的元素,但是嘿嘿,我已经说过我不认为 foldr是一个很好的方法,所以我猜这个黑客是一个不折叠的例子。

答案 2 :(得分:2)

您也可以尝试使用unfoldr代替foldr

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