2016-04-11 33 views
6

Próbuję utworzyć funkcję, która spada co n-ty element z ciągu znaków.Haskell - funkcja zwraca pusty znak

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

Teraz to po prostu zamienia każdy element n'th z miejsca, ale co mam umieścić po „inny”, jeśli chcę wrócić „pustym char”. Rozumiem, że coś takiego nie istnieje w Haskell, więc pytanie brzmi: jak mam powiedzieć Haskellowi, aby niczego nie zwracał i po prostu przejdzie do następnego znaku?

Odpowiedz

7

Nie można tego zrobić z zaledwie map z definicji nie może zmienić długości kolekcji, do której się stosuje. Możesz jednak uruchomić to bez zbyt wielu zmian, przechodząc na concatMap. Ta funkcja wymaga, aby funkcja, którą mapujesz, zwróciła listę, a następnie połączy wszystkie wyniki razem. Wszystko trzeba by zrobić, to

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

Próbowałem wstawić "[]" po "innym", ale oczywiście skończyło się otrzymaniem błędów typu. concatMap to dobry pomysł. –

+0

@ RaulŠpilev Problem wynika z założenia, że ​​istnieje coś, co nazywa się "pustą Charą". To byłoby jak "puste' Int' "lub" puste 'Bool'", możesz mieć 'Int', który wynosi 0, ale nie taki, który nie ma żadnej wartości (nie licząc' niezdefiniowanych') . Aby mieć pustkę, potrzebujesz pojemnika. Ciągi są kontenerami, ponieważ są po prostu listą 'Char's, więc mając funkcję' (Char, Int) -> [Char] ', możesz użyć' concatMap' zamiast '[(Char, Int) ], aby wydostać '[Char]'. – bheklilr

5

map zachowuje strukturę listy, a operacje modyfikują ją, usuwając elementy. Oznacza to, że nie można używać map, ale można użyć mapMaybe który pozwala zapewnić funkcję, która zwraca Nothing dla elementów, które chcesz usunąć z wyjścia:

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

musiałbym przeprowadzić kilka testów aby upewnić się, ale może to być bardziej efektywne rozwiązanie niż kopalnia – bheklilr

+0

Jest to dość dużo dokładnie to, czego szukałem. Myślałem o używaniu "Nothing" i "Just (char)", ale nie byłem pewien, jak bez uzyskania "Nie można dopasować oczekiwanego typu". "MapMaybe" rozwiązuje to. Również teraz rozumiem, jak funkcja mapy działa znacznie lepiej. Dzięki. –

3

Można to zrobić bez mod. Zamienię kolejność argumentów, aby uczynić to bardziej idiomatycznym.

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

Jeśli prędkość jest krytyczna, to prawdopodobnie najbardziej efektywne w użyciu tej techniki z wyraźną rekursji lub foldr. Coś takiego:

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