2011-09-29 18 views
5

Eventuali duplicati:
How to get every Nth element of an infinite list in Haskell?Come selezionare ogni elemento n-esimo da un elenco

compito semplice - abbiamo una lista e vogliamo lasciare solo ogni elemento n-esimo in tale elenco . Qual è il modo più idiomatico di farlo in haskell?

fuori dalla parte superiore della mia testa è qualcosa di simile:

dr n [] = [] 
dr n (x : xs) = x : (dr n $ drop n xs) 

ma ho la forte sensazione che sto overcomplicating il problema.

+2

che sembra piuttosto idiomatica a me. –

+2

non pensi che questo ti possa essere più economico - mi sembra a posto. L'unico punto è che questo non si adatta bene alla tua descrizione come 'dr k [1 ..]' non cede la sequenza delle tue domande. – Carsten

risposta

10

La soluzione è soddisfacente, ma qui ci sono altre tre soluzioni che utilizzano le funzioni dalla libreria di base di Haskell.

dr1 m = concatMap (take 1) . iterate (drop m) 

Di grossolana, questo non sarà mai terminate (perché iterate mai termina). Quindi, forse una soluzione migliore sarebbe quella di utilizzare unfoldr:

{-# LANGUAGE TupleSections #-} 
import Data.Maybe 
dr2 m = unfoldr ((\x-> fmap (,drop m x) (listToMaybe x))) 

La funzione si passa a un unfold può ottenere un po 'brutto, se non si conosce estensioni GHC e concetti come funtori, ecco che la soluzione ancora una volta senza la fantasia piede-lavoro (non testata):

dr2 m = unfoldr ((\x -> case listToMaybe x of 
         Nothing -> Nothing 
         Just i -> Just (i,drop m x))) 

Se non ti piace dipana quindi prendere in considerazione una zip e un filtro:

dr3 m = map snd . filter ((== 1) . fst) . zip (cycle [1..m]) 

Review

Comprendere tutte queste soluzioni sono leggermente diverse. Imparare perché ti farà diventare un programmatore di Haskell migliore. dr1 utilizza iterare e sarà quindi mai terminare (forse questo è ok per liste infinite, ma probabilmente non è una buona soluzione globale):

> dr1 99 [1..400] 
[1,100,199,298,397^CInterrupted. 

La soluzione dr2 mostrerà ogni valore m esimo saltando valori nel svolgersi. Lo spiegamento passa sia il valore da utilizzare per il successivo dispiegamento che il risultato dello spiegamento corrente in una singola tupla.

> dr2 99 [1..400] 
[1,100,199,298,397] 

La soluzione dr3 è leggermente più lungo, ma probabilmente più facile per un principiante da capire. Innanzitutto, tagga ogni elemento nell'elenco con un ciclo di [1..n, 1..n, 1..n ...]. In secondo luogo, si selezionano solo i numeri contrassegnati con uno 1, saltando efficacemente n-1 degli elementi. Terzo, rimuovi i tag.

> dr3 99 [1..400] 
[1,100,199,298,397] 
+0

Thomas, grazie mille. È molto importante sapere tutti i modi in cui puoi eseguire compiti relativamente semplici e mi hai appena dato alcune nuove idee. – shabunc

13

La mia variante sarebbe:

each :: Int -> [a] -> [a] 
each n = map head . takeWhile (not . null) . iterate (drop n) 

veloce e gioca bene con la pigrizia.

+0

questo è bello! – shabunc

+1

sì, è molto bello. Non volevo parlare della pigrizia (e onestamente non pensavo di aggiungere una guardia nulla), ma questa è una buona alternativa al mio primo esempio rotto. Il blocco –

8

Un sacco di modi per radersi questo yak!Ecco ancora un altro:

import Data.List.Split -- from the "split" package on Hackage 
dr n = map head . chunk n 
+0

è implementato in modo molto simile, in realtà '- | divisa a intervalli regolari pezzo :: Int -> [a] -> [[a]] pezzo _ [] = [[]] pezzo n xs = Y1: pezzo n y2 dove (Y1, Y2) = splitAt n xs' – shabunc

+0

@shabunc Hai scommesso! Il riutilizzo del codice è il nome del gioco. –

0

Prova questa:

getEach :: Int -> [a] -> [a] 
getEach _ [] = [] 
getEach n list 
| n < 1  = [] 
| otherwise = foldr (\i acc -> list !! (i - 1):acc) [] [n, (2 * n)..(length list)] 

Poi nel GHC:

*Main> getEach 2 [1..10] 
[10,8,6,4,2] 
+1

Questa soluzione non funziona per gli elenchi infiniti (a causa di 'length list'); perde spazio (forza la lista della colonna vertebrale, ma non rilascia l'inizio della lista finché non termina); è inefficiente nel recuperare elementi dalla lista ('!!' è O (n); questo è un [algoritmo di Schlemiel the Painter] (http://en.wikipedia.org/wiki/Schlemiel_the_Painter%27s_algorithm)); ed è troppo complesso. – dave4420

Problemi correlati