GHCi is frequently significantly slower than compiled code even if you compile without any optimizations turned on, consider the timing information that GHCi gives you with great suspicion. Compile your code with
-O2 and then time and the difference should largely disappear.
One of the nice things about the Haskell Prelude is that there is very little magic going on behind the scenes. Haskell lists use some privileged constructor names (
) but besides that they are a data type like any other. So let's peek behind the curtain and take a look at how
(++) is defined.
(++) :: [a] -> [a] -> [a]
(++)  ys = ys
(++) (x:xs) ys = x : xs ++ ys
You should recognize the similarity to your function
f. There is no way around the fact that growing a list by
n elements takes
n cons operations. When you want to add one element to the front of a list, you use
(:) and that's it, you're done. When you want to add one element to the back of a list there's no constructor for that, you have to consider that element to be a new list of length one, and prepend your original list in front of it, causing you to have to walk down every single element of the original list. That is “slower” than the other case, but if the ordering of elements in your list is important you gotta do what you've gotta do, right?
Take some time to familiarize yourself with the functions available in
Data.List. There are many easier ways to write
As always, consider the types and use Hoogle. Looking at
take n . repeat $ x think about the type you would give that function on its own.
n is an
x is any type
a. So you'd have a type like
func :: Int -> a -> [a]. The first hit is for
replicate, which does exactly what you want.
Next consider what easy intermediate values you can construct that get you closer to the result you want. The first step would be to replicate each element in the original list
n times. That sounds like a job for
map (replicate n) xs :: [[a]]
Now we need
:: [[a]] -> [a], which we could also search for. But this is of course list concatenation. There's one more trick you might not find by searching though, so here is when it comes in handy to have read through the documentation for
concatMap handles mapping and list concatenation in one go.
explodeBy :: Int -> [a] -> [a]
explodeBy n = concatMap (replicate n)