当我们折叠包含一个或多个元素的列表时,如下所示:
foldr (+) 0 [1,2,3]
我们得到:
foldr(+)0(1:2:3:[])
foldr(+)1 +(2 +(3 + 0))// 6
现在当列表为空时:
foldr (+) 0 []
结果:foldr(+)0([])
由于(+)是二元运算符,它需要两个参数才能完成,但在这里我们最终得到(+) 0
。它是如何导致0而不是抛出部分应用函数的错误。
答案 0 :(得分:4)
简短回答:您获得初始值z
。
如果您为foldl
或foldr
提供一个空列表,则会返回初始值。 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
只查看初始值,然后返回值,然后可以删除递归部分。