2016-04-11 13 views
6

Sto provando a creare una funzione che cancelli ogni n elemento da una stringa.Haskell - ha una funzione restituisce un carattere vuoto

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

In questo momento sostituisce semplicemente ogni elemento n'th con uno spazio, ma cosa dovrei mettere dopo il "resto" se voglio che ritorni un "char vuoto". Capisco che una cosa del genere non esista in Haskell quindi la domanda è: come dovrei dire a Haskell di non restituire nulla e passare al prossimo char?

risposta

7

Non si può fare questo con un solo map , per definizione, non può modificare la lunghezza della collezione a cui è applicato. Tuttavia, puoi farlo funzionare senza troppe modifiche passando a concatMap. Questa funzione richiede che la funzione che stai mappando restituisca un elenco, quindi concatena tutti i risultati insieme. Tutto quello che avresti bisogno di fare è

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

Ho provato a inserire "[]" dopo "else", ma naturalmente ho finito per ottenere errori di tipo. concatMap è una buona idea. –

+0

@ RaulŠpilev Il problema deriva dal presupposto che ci sia qualcosa chiamato "empty' Char' ". Questo sarebbe come un "vuoto' Int' "o un" vuoto 'Bool'", puoi avere un 'Int' che è 0, ma non uno che non ha alcun valore (senza contare' indefinito') . Per avere il vuoto, hai bisogno di un contenitore. Le stringhe sono contenitori, poiché sono semplicemente una lista di 'Char's, quindi avendo una funzione' (Char, Int) -> [Char] ', puoi usare' concatMap' su un '[(Char, Int) ] per ottenere un '[Char]'. – bheklilr

5

map conserva la struttura dell'elenco, mentre le operazioni lo modificano rimuovendo gli elementi. Questo significa che non è possibile utilizzare map, ma è possibile utilizzare mapMaybe che permette di fornire una funzione che restituisce Nothing per gli elementi che si desidera rimuovere dal uscita:

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

avrei dovuto eseguire alcuni test per essere sicuri, ma questa potrebbe essere una soluzione più efficiente della mia – bheklilr

+0

Questo è più o meno esattamente quello che stavo cercando. Stavo pensando di usare "Nothing" e "Just (char)", ma non ero sicuro di come ottenere senza errori "Could not match expected type". "MapMaybe" risolve questo problema. Inoltre, ora capisco come funziona la funzione mappa molto meglio. Grazie. –

3

È possibile fare questo senza mod. Vado a girare l'ordine degli argomenti per renderlo più idiomatico.

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

Se la velocità è critica, sarebbe probabilmente più efficace per utilizzare questa tecnica con la ricorsione esplicita o foldr. Qualcosa di simile a questo:

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