Some exercise required me to write a piece of code to double every second item in a list. My first thought was just using cons and I came up with the following:
doubleSecond :: Num a => [a] -> [a]
doubleSecond [] = []
doubleSecond [x] = [x]
doubleSecond (x:y:xs) = x : (y * 2) : doubleSecond xs
While it's straightforward and does work, it just doesn't look that elegant and I'm curious how this could be rewritten in a more elegant fashion :)
1 Answer 1
Built-ins and generality
It is a good first try, but I discourage explicit recursion and suggest a larger use of built-ins, Haskell provides you with incredibly powerful and general functions. Also writing something a bit more general will make you familiar with first class functions (the core of the Haskell experience).
Decomposition
As always, I am going to divide the task in smaller, more manageable parts:
- Find out how to apply a function to each second item of list.
- Give
multiply_by_two
as argument to the function above.
mapSecond
mapSecond :: (a -> a) -> [a] -> [a]
mapSecond f = zipWith ($) (cycle [id, f])
Given a function and a list, apply the function to each second item of the list.
This function is quite advanced, let me explain it:
zipWith
: given a function and two list applies the function to each pair. If you are familiar with Python,zip = zipWith (,)
Prelude> zipWith (++) ["Hello", "Foo"] ["World", "Bar"]
["HelloWorld","FooBar"]
$
: given a function and two arguments, applies it to the arguments.
Prelude> ($) (*) 2 5
10
cycle
: given a list repeats it forever.
Prelude> take 10 $ cycle [1,2,3]
[1,2,3,1,2,3,1,2,3,1]
The second argument is omitted, as it can be 'currried' (simplified) away.
The main
function is now:
main = print $ ( mapSecond (* 2) ) [1,2,3,4,5,6]