19
votes

I just started using Haskell and wanted to write a function that, given a list, returns a list in which every 2nd element has been doubled.

So far I've come up with this:

double_2nd :: [Int] -> [Int]
double_2nd [] = []
double_2nd (x:xs) = x : (2 * head xs) : double_2nd (tail xs)

Which works but I was wondering how you guys would write that function. Is there a more common/better way or does this look about right?

6
I think your code above lacks the case where the list contains a single element. I would add double_2nd [x] = [x] after the [] = [] match - Pejvan

6 Answers

43
votes

That's not bad, modulo the fixes suggested. Once you get more familiar with the base library you'll likely avoid explicit recursion in favor of some higher level functions, for example, you could create a list of functions where every other one is *2 and apply (zip) that list of functions to your list of numbers:

double = zipWith ($) (cycle [id,(*2)])
21
votes

You can avoid "empty list" exceptions with some smart pattern matching.

double2nd (x:y:xs) = x : 2 * y : double2nd xs
double2nd a = a

this is simply syntax sugar for the following

double2nd xss = case xss of
    x:y:xs -> x : 2 * y : double2nd xs
    a -> a

the pattern matching is done in order, so xs will be matched against the pattern x:y:xs first. Then if that fails, the catch-all pattern a will succeed.

10
votes

A little bit of necromancy, but I think that this method worked out very well for me and want to share:

double2nd n = zipWith (*) n (cycle [1,2])

zipWith takes a function and then applies that function across matching items in two lists (first item to first item, second item to second item, etc). The function is multiplication, and the zipped list is an endless cycle of 1s and 2s. zipWith (and all the zip variants) stops at the end of the shorter list.

5
votes

Try it on an odd-length list:

Prelude> double_2nd [1]
[1,*** Exception: Prelude.head: empty list

And you can see the problem with your code. The 'head' and 'tail' are never a good idea.

0
votes

For odd-lists or double_2nd [x] you can always add

    double_2nd (x:xs) | length xs == 0 = [x] 
                      | otherwise = x : (2 * head xs) : double_2nd (tail xs)

Thanks.

0
votes

Here's a foldr-based solution.

bar :: Num a => [a] -> [a]
bar xs = foldr (\ x r f g -> f x (r g f)) 
               (\ _ _ -> []) 
               xs 
               (:)
               ((:) . (*2))

Testing:

> bar [1..9]
[1,4,3,8,5,12,7,16,9]