paulgavrikov
paulgavrikov

Reputation: 1891

Haskell duplicate element at certain position

I want to duplicate the n-th elemnt of a list and my knowledge of haskell is very limited. I tried splitting the list in two parts, then getting the last element of the first part and just pasting it between those parts:

dupl n (x:xs) = (take n (x:xs)) ++ ( (x:xs) !! n) ++ (drop n (x:xs))

But I always get the error:

Prelude> :l f.hs
[1 of 1] Compiling Main             ( f.hs, interpreted )

f.hs:5:39:
   Occurs check: cannot construct the infinite type: a0 = [a0]
   In the first argument of `(:)', namely `x'
   In the first argument of `(!!)', namely `(x : xs)'
   In the first argument of `(++)', namely `((x : xs) !! n)'
Failed, modules loaded: none.

Could someone tell me what I am doing wrong?

Upvotes: 3

Views: 704

Answers (3)

Benjamin Hodgson
Benjamin Hodgson

Reputation: 44634

list !! n returns a list element, not a list. ++ can only concatenate lists; you can't add a list element to a list with ++.

To put it more formally, !! has a type of:

(!!) :: [a] -> Int -> a

which means that it accepts a list of as and an Int, and returns an a. ++, on the other hand, has a type of:

(++) :: [a] -> [a] -> [a]

which means that it accepts two lists of as and returns a new list of as. So you can see that ++ accepts lists, but !! doesn't return lists, so you're not allowed to chain them together like that.


How do we fix this? You need to put the element list !! n into a list, in order to concatenate it to the other two lists without the type checker throwing a hissy fit.

This should do the trick:

dupl n l = (take n l) ++ [l !! n] ++ (drop n l)

Equivalently, add the chosen element to the right-hand list, and concatenate that to the other half of the original list:

dupl n l = (take n l) ++ ((l !! n):(drop n l))

Caveat lector: both of the above functions, unlike @Marimuthu's suggestion, will raise an exception if n is an out-of-bounds index. The exception comes from !!.

Upvotes: 4

J. Abrahamson
J. Abrahamson

Reputation: 74374

Caveat emptor if you pick a bad index, but—

dupN n xs = let (first, x:rest) = splitAt (n-1) xs in first ++ x:x:rest

Upvotes: 2

Marimuthu Madasamy
Marimuthu Madasamy

Reputation: 13541

You can also use splitAt:

dupl n xs = first ++ take 1 rest ++ rest where (first, rest) = splitAt (n - 1) xs 

This will not break if n is out of bounds.

Upvotes: 1

Related Questions