Insert a specific element y after each n elements in the list

I teach myself Haskell, and I came across a question in my book that asked me to define an insert function that takes a positive integer n , element y and a list xs that inserts the specified element y after each element n in the list.

I think matching patterns is likely to be a good way, but I haven't figured out what this means.

 insert :: Int -> Char -> [a] -> [a] insert 0 y xs = xs insert ny [] = [] insert ny (x:xs) 

An example of how the function should work:

 insert 2 'X' "abcdefghijk" = "abXcdXefXghXijXk" 

I took care of the basic cases at this point, but I do not know how to proceed from here.

Any ideas? Thanks

+4
source share
4 answers

You can write a helper function that counts and resets when it reaches zero.

 insert :: Int -> a -> [a] -> [a] insert ny xs = countdown n xs where countdown 0 xs = y:countdown n xs -- reset to original n countdown _ [] = [] countdown m (x:xs) = x:countdown (m-1) xs 

What behavior do you want if it is time to insert at the end? Here I put the priority on pasting over the finish, setting countdown 0 xs to countdown _ [] . How could you rewrite it if you want to skip the insert at the end?

Sample Usage:

 *Main> insert 3 '|' "Hello Mum, erm... can I borrow £20000 please?" "Hel|lo |Mum|, e|rm.|.. |can| I |bor|row| £2|000|0 p|lea|se?|" 
+3
source

In the latter case, take n list items, insert one list from y, and then add the result of a recursive function call after deleting the first n list items.

 insert :: Int -> Char -> [a] -> [a] insert 0 y xs = xs insert ny [] = [] insert ny xs | length xs < n = xs | otherwise = take n xs ++ [y] ++ insert ny (drop n xs) 
+4
source

It's good to use library features to your advantage.

 import Data.List insertAtN ny xs = intercalate [y] . groups n $ xs where groups n xs = takeWhile (not.null) . unfoldr (Just . splitAt n) $ xs 

Of course, if you type Char into a list of type [a] , then a will be Char , because in Haskell all the elements of the list are of the same type.


To help you understand this more directly, let's first see how easy it is to make a copy of the list:

 copyList (x:xs) = x : copyList xs copyList [] = [] 

Now imagine that you are adding an index value to each element to be copied (reimplementing zip xs [1..] ):

 copyIdxList xs = go 1 xs where go i (x:xs) = (x,i) : go (i+1) xs go _ [] = [] 

Now that we have the index value, when we deal with each element, we can use it and, say, put every 10th element of the list twice in the result:

 copyIdxTenthTwice xs = go 1 xs where go i (x:xs) | i==10 = (x,i) : (x,i) : go 1 xs go i (x:xs) = (x,i) : go (i+1) xs go _ [] = [] 

See where I am going? Instead of duplicating x you can insert y there. And you do not need to specify indexes in the result.

+3
source
 ins ny xs = zip xs (cycle [1..n]) >>= f where f (x,k) = if k == n then [x,y] else [x] 

The zip part attaches circular "indexes" to list items, for example. for n = 3 and xs = "abcdefg" we get [('a',1),('b',2)('c',3)('d',1)('e',2)('f',3)('g',1)] . Now (>>=) (which is the same as concatMap in the case of lists) uses f to map each pair back to the original element, except when we have the last index of the loop: in this case, we insert an additional separator element y .

+1
source

Source: https://habr.com/ru/post/1437014/


All Articles