简体   繁体   English

foldl / foldr查询

[英]foldl / foldr query

I'm a beginner at Haskell, and even after reading several explanations of foldr/foldl, I can't understand why I'm getting different results below. 我是Haskell的初学者,即使在阅读了foldr / foldl的几个解释之后,我也无法理解为什么我会在下面得到不同的结果。 What is the explanation? 解释是什么?

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

Thanks! 谢谢!

That's because the order of the arguments is flipped in foldl . 那是因为参数的顺序在foldl翻转。 Compare their type signatures: 比较他们的类型签名:

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

So you see, in your code using foldl , you repeatly increment the accumulator, ignoring the list. 所以你看,在使用foldl的代码中,你重复递增累加器,忽略列表。 But in the code with foldr , you don't even touch the accumulator, but just increment the element of the list. 但是在带有foldr的代码中,你甚至没有触及累加器,只是递增列表的元素。 As the last element is 3 , the result is 3 + 1 = 4 . 由于最后一个元素是3 ,结果是3 + 1 = 4

You could see your misstake more easy, if you'd use a list of characters aka string instead: 如果您使用字符列表而不是字符串,您可以更容易地看到您的错误:

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>

In the foldl case, the lambda is being passed the accumulator as the first argument, and the list element as the second. foldl情况下,lambda作为第一个参数传递累加器,而第二个参数传递list元素。 In the foldr case, the lambda is being passed the list element as the first argument, and the accumulator as the second. foldr情况下,lambda作为第一个参数传递list元素,而第二个参数传递累加器。

Your lambda ignores the first argument, and adds 1 to the second, so in the foldl case you are adding 1 to the last element, and in the foldr case, you are counting the number of elements in the list. 你的lambda忽略第一个参数,并在第二个参数中加1,所以在foldl情况下,你在最后一个元素中加1,而在foldr情况下,你在计算列表中的元素个数。

In foldl f , the accumulator is the left argument to f , which you are ignoring, therefore returning 1 + the last element. foldl f ,累加器是左参数f ,您所忽略,因此返回1 +的最后一个元素。 With foldr , the accumulator is the right argument, so you're increasing the accumulator by 1 for every item, effectively giving the length of the list. 对于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

The difference is because of two things: 不同之处在于两件事:

  • You're discarding one input to the accumulating function and applying a constant function to the other. 您正在丢弃累加函数的一个输入并将常量函数应用于另一个。
  • The order of arguments to the accumulating function are different between the two. 累加函数的参数顺序在两者之间是不同的。

With the left fold, the accumulator is the argument you discard, so each time you're applying (+1) to the next item in the list and eventually return the last element plus one. 使用左侧折叠,累加器是您丢弃的参数,因此每次将(+1)应用于列表中的下一个项目并最终返回最后一个元素加一个。

With the right fold, the accumulator is the argument you keep, so each time you're applying (+1) to the previous result, which is 0 to start with and gets incremented three times (for each item in the list). 使用右侧折叠,累加器是您保留的参数,因此每次将(+1)应用于前一个结果时,从0开始并增加三次(对于列表中的每个项目)。

It might be easier to see what's going on here if you use more obviously distinct input values: 如果你使用更明显不同的输入值,可能更容易看到这里发生了什么:

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

Again, "last argument plus one" and "length of list plus initial value". 再次,“最后一个参数加一个”和“列表长度加上初始值”。

Removing the currying and point free style may help. 去除currying和point自由样式可能会有所帮助。 Your two expressions are equivilent to: 你的两个表达式是等效的:

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

When viewed as such the results should appear more obvious 当这样看时,结果应该显得更加明显

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM