foldl / foldr查询

时间:2011-05-18 19:58:08

标签: haskell fold

我是Haskell的初学者,即使在阅读了foldr / foldl的几个解释后,我也无法理解为什么我会在下面得到不同的结果。解释是什么?

Prelude> foldl (\_ -> (+1)) 0 [1,2,3]
4
Prelude> foldr (\_ -> (+1)) 0 [1,2,3]
3

谢谢!

5 个答案:

答案 0 :(得分:7)

那是因为参数的顺序在foldl中翻转。比较他们的类型签名:

foldl :: (a -> b -> a) -> a -> [b] -> a
foldr :: (a -> b -> b) -> b -> [a] -> b

所以你看,在使用foldl的代码中,你重复递增累加器,忽略列表。但是在使用foldr的代码中,您甚至不会触及累加器,而只是增加列表的元素。由于最后一个元素是3,因此结果为3 + 1 = 4

如果您使用字符列表而不是字符串,您可以更容易地看到您的错误:

ghci> foldr (\_ -> (+1)) 0 ['a','b','c']
3
ghci> foldl (\_ -> (+1)) 0 ['a','b','c']

:1:20:
    No instance for (Num Char)
      arising from the literal `0'
    Possible fix: add an instance declaration for (Num Char)
    In the second argument of `foldl', namely `0'
    In the expression: foldl (\ _ -> (+ 1)) 0 ['a', 'b', 'c']
    In an equation for `it':
        it = foldl (\ _ -> (+ 1)) 0 ['a', 'b', 'c']
ghci>

答案 1 :(得分:7)

foldl的情况下,lambda作为第一个参数传递累加器,而第二个参数传递list元素。在foldr的情况下,lambda作为第一个参数传递list元素,而第二个参数传递累加器。

你的lambda忽略了第一个参数,并且在第二个参数中加1,所以在foldl情况下你将1添加到最后一个元素,而在foldr情况下,你正在计算数字列表中的元素。

答案 2 :(得分:6)

foldl f中,累加器是f的左参数,您忽略它,因此返回1 +最后一个元素。使用foldr,累加器是正确的参数,因此您将每个项目的累加器增加1,从而有效地给出列表的长度。

f x y = y + 1

foldl f 0 [1, 2, 3]
= f (f (f 0 1) 2) 3 
= f ignored 3
= 3 + 1
= 4

foldr f 0 [1, 2, 3]
= f 1 (f 2 (f 3 0)))
= f ignored (f ignored (f ignored 0)))
= ((((0 + 1) + 1) + 1)
= 3

答案 3 :(得分:6)

不同之处在于两件事:

  • 您正在丢弃累积函数的一个输入并将常量函数应用于另一个。
  • 累积函数的参数顺序在两者之间是不同的。

使用左侧折叠,累加器是您丢弃的参数,因此每次将(+1)应用于列表中的下一个项目并最终返回最后一个元素加上一个。

使用右侧折叠,累加器是您保留的参数,因此每次将(+1)应用于前一个结果时,该结果为0并且增加三次(对于每个项目,列表)。

如果您使用更明显不同的输入值,可能更容易看到这里发生了什么:

Prelude> foldl (\_ -> (+1)) 100 [5,6,7]
8
Prelude> foldr (\_ -> (+1)) 100 [5,6,7]
103

再次,“最后一个参数加一个”和“列表长度加上初始值”。

答案 4 :(得分:4)

去除currying和point free样式可能会有所帮助。你的两个表达式相当于:

foldl (\acc x ->   x + 1) 0 [1,2,3]
foldr (\x acc -> acc + 1) 0 [1,2,3]

当这样看时,结果应该显得更加明显