问题
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