Question

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.

Was it helpful?

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)

OTHER TIPS

scanl and scanr are used to show the value of the accumulator on each iteration. scanl iterates from left-to-right, and scanr from right-to-left.

Consider the following example:

scanl (+) 0 [1, 2, 3]

-- 0. `scanl` stores 0 as the accumulator and in the output list [0]
-- 1. `scanl` adds 0 and 1 and stores 1 as the accumulator and in the output list [0, 1]
-- 2. `scanl` adds 1 and 2 and stores 3 as the accumulator and in the output list [0, 1, 3]
-- 3. `scanl` adds 3 and 3 and stores 6 as the accumulator and in the output list [0, 1, 3, 6]
-- 4. `scanl` returns the output list [0, 1, 3, 6]

As you can see, scanl stores the results of the accumulator while it's iterating through the list. This is the same for scanr, but the list is iterated in reverse.

Here's another example:

scanl (flip (:)) [] [1, 2, 3]

-- [[], [1], [2,1], [3,2,1]]

scanr       (:)  [] [1, 2, 3]

-- [[1,2,3], [2,3], [3], []]
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top