perform a function on first elements of list

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

Answers (1)

chi
chi

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

Related Questions