折叠如何适用于空列表?

时间:2017-06-21 11:29:14

标签: haskell fold

当我们折叠包含一个或多个元素的列表时,如下所示:

foldr (+) 0 [1,2,3]
  

我们得到:

     

foldr(+)0(1:2:3:[])

     

foldr(+)1 +(2 +(3 + 0))// 6

现在当列表为空时:

 foldr (+) 0 []
  

结果:foldr(+)0([])

由于(+)是二元运算符,它需要两个参数才能完成,但在这里我们最终得到(+) 0。它是如何导致0而不是抛出部分应用函数的错误。

1 个答案:

答案 0 :(得分:4)

简短回答:您获得初始值z

如果您为foldlfoldr提供一个空列表,则会返回初始值。 foldr :: (a -> b -> b) -> b -> t a -> b的作用类似于:

foldr f z [x1, x2, ..., xn] == x1 `f` (x2 `f` ... (xn `f` z)...)

因为没有x1,...,xn,所以从不应用该函数,并返回z

我们还可以检查source code

foldr            :: (a -> b -> b) -> b -> [a] -> b
-- foldr _ z []     =  z
-- foldr f z (x:xs) =  f x (foldr f z xs)
{-# INLINE [0] foldr #-}
-- Inline only in the final stage, after the foldr/cons rule has had a chance
-- Also note that we inline it when it has *two* parameters, which are the
-- ones we are keen about specialising!
foldr k z = go
          where
            go []     = z
            go (y:ys) = y `k` go ys

因此,如果我们给foldr一个空列表,那么go将立即对该空列表起作用,并返回z,即初始值。

更清晰的语法(效率稍低,如函数注释中所写)将是:

foldr :: (a -> b -> b) -> b -> [a] -> b
foldr _ z [] = z
foldr f z (x:xs) = f x (foldr f z xs)

请注意 - 根据f的实现 - 可以在无限列表上foldr:如果在某个时刻f只查看初始值,然后返回值,然后可以删除递归部分。