Reputation: 2886
In an attempt to write the Havel-Hakimi algorithm in Haskell, I wrote following function to subtract 1 from the first n elements of a list.
decrease_first :: Int -> [Int] -> [Int]
decrease_first 0 l = l
decrease_first n (x:xs) = (x-1):decrease_first (n-1) xs
This code works, but as I am trying to prepare for a Haskell-test and I was convinced this code is rather lame, I wanted to rewrite this function as follows:
decrease_first :: Int -> [Int] -> [Int]
decrease_first = map (subtract1) . take
I'm aware of the fact this does not do exactly the same, but I was just trying to understand partially applied functions etc. better and wanted to give this function a try. This turned out not to be the greatest of ideas, because I didn't get this compiled (type-definitions) and I am left with a massive mind-you-know-what, because next code does work, even though I considered it equivalent:
decrease_first n = map (subtract1) . take n
and this code fails again:
decrease_first n l = map (subtract1) . take n l
Moreover I tried looking for some nice way to apply a function to the first elements of a list, but I couldn't find anything. The first way is probably the most efficient way to do it, but I was wondering whether there was some way to apply any function on the first elements of a list. My idea eventually was to do something like:
decrease_first n l = map (subtract 1) (take n l) ++ drop n l
It works like this, but it doesn't look as nice as I had it in mind. So if anyone could help me resolving my type-issue, I would be very grateful.
Thanks in advance
Upvotes: 1
Views: 2377
Reputation: 116174
Your first point-free attempts don't work because .
composes functions with one argument, while take
takes two. What really happens is that code like
decrease_first = map (subtract1) . take
is equivalent to
decrease_first n = map (subtract1) (take n)
but now take n
is not a list, hence a type error arises.
Instead, in
decrease_first n l = map (subtract1) . take n l
we have that take n l
is a list, not a function. Here you need application, not function composition e.g.
decrease_first n l = map (subtract1) $ take n l
Your last attempt looks fine to me:
decrease_first n l = map (subtract 1) (take n l) ++ drop n l
As a variant, you can use splitAt
to get take
&drop
together:
import Data.List
decrease_first n l = map (subtract 1) left ++ right
where (left,right) = splitAt n l
or alternatively:
decrease_first n l =
(\(left, right) -> map (subtract 1) left ++ right) (splitAt n l)
which is not really better than the above ones.
Upvotes: 3