Raul Špilev
Raul Špilev

Reputation: 378

Haskell - have a function return an empty character

I'm trying to create a function that drops every n'th element from a string.

dropEvery :: String -> Int -> String
dropEvery str n = map (\(char, indx) -> if indx `mod` n /= 0 then char else ' ') (zip str [1..])

Right now it simply replaces every n'th element with a space, but what am I supposed to put after the "else" if I want it to return an "empty char". I understand that such a thing doesn't exist in Haskell so the question is - how am I supposed to tell Haskell to not return anything and just move on to the next char?

Upvotes: 6

Views: 2522

Answers (3)

dfeuer
dfeuer

Reputation: 48591

You can do this without mod. I'm going to flip the order of the arguments to make this more idiomatic.

dropEvery :: Int -> [a] -> [a]
dropEvery n xs = map fst . filter ((/= n) . snd) $ zip xs (cycle [1..n])

If speed is critical, it would likely be most efficient to use this technique with explicit recursion or foldr. Something like this:

dropEvery n xs = foldr go (`seq` []) xs n where
  go _ r 1 = r n
  go x r k = x : r (k - 1)

Upvotes: 3

bheklilr
bheklilr

Reputation: 54058

You can't do this with just map, by definition it can't change the length of the collection it's applied to. However, you can get this to work without too many changes by switching to concatMap. This function requires the your function that you're mapping return a list, then it concatenates all the results together. All you'd need to do is

dropEvery str n =
    concatMap (\(char, indx) -> if indx `mod` n /= 0 then [char] else []) (zip str [1..])

Upvotes: 7

Lee
Lee

Reputation: 144136

map preserves the structure of the list, while your operations modifies it by removing elements. This means you can't use map, but you can use mapMaybe which allows you to provide a function which returns Nothing for elements you want to remove from the output:

import Data.Maybe (mapMaybe)
dropEvery str n = mapMaybe (\(char, indx) -> if indx `mod` n /= 0 then Just(char) else Nothing) (zip str [1..])

Upvotes: 5

Related Questions