foldl / foldr query

删除回忆录丶 提交于 2019-12-08 15:53:47

问题


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. What is the explanation?

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

Thanks!


回答1:


In the foldl case, the lambda is being passed the accumulator as the first argument, and the list element as the second. In the foldr case, the lambda is being passed the list element as the first argument, and the accumulator as the second.

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.




回答2:


That's because the order of the arguments is flipped in 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. But in the code with foldr, you don't even touch the accumulator, but just increment the element of the list. As the last element is 3, the result is 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>



回答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.

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).

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".




回答4:


In foldl f, the accumulator is the left argument to f, which you are ignoring, therefore returning 1 + the last element. 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.

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



回答5:


Removing the currying and point free style may help. 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



来源:https://stackoverflow.com/questions/6050306/foldl-foldr-query

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!