Matt Robbins
Matt Robbins

Reputation: 449

Alternating series using Haskell

I am trying to use Haskell to read in a list and perform an alternating series, so starting with the first element adding every other and subtracting every other from the second element. For instance [1, 2, 3, 4] would be 1-2+3-4=-2. I thought I had figured out how to do this for lists of a specific length (I wrote it to accommodate empty lists and lists up to 4 elements long), but it doesn't do what I thought it would. It just returns the first element from the list. Here is what I have:

altSeries :: Num a => [a] -> a
altSeries [] = 0
altSeries (x:xs) = if length xs == 1 then x
                   else if length xs == 2 then x
                   else if length xs == 3 then (x - xs!!1 + xs!!2)
                   else (x - xs!!1 + xs!!2 - xs!!3)

Also, what if I wanted to be able to use any list size?

Upvotes: 2

Views: 1392

Answers (2)

Rainbacon
Rainbacon

Reputation: 943

What you want is a list that ultimately looks something like this [1,-2,3,-4] which you could sum.

You can make a list of alternating sign [1,-1]. this can be made infinite using the cycle function. let alternatingSigns = cycle [1,-1]

To transform a list of [1,2,3,4] you can zip the infinite alternating list with your list like this zipWith (*) alternatingSigns input

The whole function would look something like this:

altSeries :: Num a => [a] -> a
altSeries input = let alternatingSigns = cycle [1,-1]
                  in sum $ zipWith (*) alternatingSigns input

Upvotes: 5

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477210

I think it is clear that this solution does not scale: even if you would write up function up to lists with length thousand, it would break from the moment that someone enters a list with thousand and one elements.

List processing usually is done recursively: we process the head (or the first k heads) of a list, and perform recursion on the tail of the list. Furthermore we have a number of basecases (can be one) to terminate recursion.

You already provided a single base case: the empty list:

altSeries [] = 0

In case we enter an empty list, it is clear that we should return 0. Now we might ask what to do in case we obtain a list of length 1: in that case the list has shape [x]. So in that case we should return x, since we should add x, and can not subtract the second number. So we add:

altSeries [x] = x

Now the question is what to do with lists with length two or more. In that case the list has pattern (x1:x2:t) with x1 the first element, x2 the second element, and t the remaining elements. Based on your description we should caculate x1-x2 and add altSeries t:

altSeries (x1:x2:t) = x1 - x2 + altSeries t

This works since altSeries will then recursively extract x3 and x4, so then it will recurse to:

altSeries [x1,x2,x3,x4,x5] = x1 - x2 + altSeries [x3,x4,x5]
                           = x1 - x2 + x3 - x4 + altSeries [x5]
                           = x1 - x2 + x3 - x4 + x5

So the full implementation is:

altSeries :: Num n => [n] -> n
altSeries [] = 0
altSeries [x] = x
altSeries (x1:x2:t) = x1 - x2 + altSeries t

Upvotes: 4

Related Questions