Search code examples
haskellhigher-order-functionsfold

How can I drop nth element of a list using foldl?


dropnth' :: [a] -> Int -> [a]
dropnth' xs n = foldl (\a b -> if (last a) == xs!!n then a else b ++ []) [head xs] xs

I was trying to solve this "dropping every nth element of a list" question using foldl, but I'm getting an error. How can I do that?

Error: The error


Solution

  • a are presumably the elements that you have already decided not to drop. You should then decide whether to drop, not the last element of a, but the next element in xs, which is presumably b.

    b ++ [] is presumably meant to express that you have decided not to drop the element b, instead adding it to the list a. This is actually written a ++ [b].

    This allows me to write this piece of code, which at least compiles:

    dropnth' :: Eq a => [a] -> Int -> [a]
    dropnth' xs n = foldl (\a b -> if b == xs!!n then a else a ++ [b]) [head xs] xs
    

    xs!!n finds the nth element of xs, and comparing with that will find decide whether something's value is equal to that, not something's position. Note the Eq a, which tells us that we are comparing list values. foldl will have to get the positions of the entries from somewhere, such as from zip [0..].

    dropnth' :: [a] -> Int -> [a]
    dropnth' xs n = foldl (\a (i, b) -> if mod i n == 0 then a else a ++ [b]) [head xs] (zip [0..] xs)
    

    Adding an element to the end of a list has to rebuild the whole list. Building the list up from its end would be much more efficient. But in this case, we can even use more specialized list operations for our use case.

    dropnth' :: [a] -> Int -> [a]
    dropnth' xs n = [b | (i, b) <- zip [0..] xs, mod i n > 0]
    

    Note that we now drop the initial element as well. Perhaps that is what you want? Or you could zip with [1..] instead to shift all the crosshairs one to the left.

    Usually, type signatures like Int -> [a] -> [a] compose better.