问题
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?
回答1:
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..])
回答2:
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..])
回答3:
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)
来源:https://stackoverflow.com/questions/36554499/haskell-have-a-function-return-an-empty-character