Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 887
Thanatos Avatar asked Jul 30 '13 14:07

Thanatos


Video Answer


2 Answers

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)
like image 128
Will Ness Avatar answered Sep 20 '22 20:09

Will Ness


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], []]
like image 20
Edwin Pratt Avatar answered Sep 18 '22 20:09

Edwin Pratt