Search code examples
listfunctionhaskellhigher-order-functionsfold

How does scanr work? Haskell


I have been messing with some Haskell functions, some I have understand and some don't.

For example if we do: scanl (+) 0 [1..3] my understanding is the following:

1. the accumulator is 0                  acc         = 0    |
2. (+) applied to acc and first el       acc = 0 + 1 = 1    |
3. (+) applied to latest acc and snd el  acc = 1 + 2 = 3    |
4. (+) applied to latest acc and third   acc = 3 + 3 = 6    V

Now when we make the list we get [0, 1, 3, 6].

But I can't seem to understand how does scanr (+) 0 [1..3] gives me: [6,5,3,0] Maybe scanr works the following way?

1. the first element in the list is the sum of all other + acc
2. the second element is the sum from right to left (<-) of the last 2 elements
3. the third element is the sum of first 2...

I don't see if that's the pattern or not.


Solution

  • scanr is to foldr what scanl is to foldl. foldr works from the right:

    foldr (+) 0 [1,2,3] =
      (1 + (2 + (3 +   0))) =
      (1 + (2 +    3)) =
      (1 +    5) =
         6
    -- [ 6,   5,   3,   0 ]
    

    and scanr just shows the interim results in sequence: [6,5,3,0]. It could be defined as

    scanr (+) z xs = foldr g [z] xs
      where
      g x ys@(y:_) = x+y : ys
    

    scanl though should work like

    scanl (+) 0 [1,2,3] =
      0 : scanl (+) (0+1) [2,3] =
      0 : 1 : scanl (+) (1+2) [3] =
      0 : 1 : 3 : scanl (+) (3+3) [] =
      0 : 1 : 3 : [6]
    

    so it must be that

    scanl (+) z xs = foldr f h xs z
       where h      z = [z]
             f x ys z = z : ys (z + x)