2014-09-25 22 views
6

È molto comune in cui trovo alcuni elenchi di elementi xs e voglio fare qualcosa per fare qualcosa con ogni ennesimo elemento. L'esempio più semplice sarebbe il Sieve o Erastothenes, in cui si vuole "mettere fuori combattimento" ogni multiplo di un dato primo. I due modi in cui potrei fare ciò sarebbero la ricorsione esplicita che passa lungo una variabile contatore; o zipWith ($) (cycle (replicate (n-1) id ++ f)). In che modo è migliore/più elegante/più comunemente usato, o c'è qualche funzione di libreria come mapEveryN :: (a -> a) -> Int -> [a] -> [a] che non ho trovato?In Haskell, qual è il modo più comune per applicare una funzione ad ogni ennesimo elemento di una lista?

+3

Bene, si potrebbe fare 'zipWith ($) (drop 1 $ cycle $ f: replica (n - 1) id)', che sembra essere piuttosto elegante per me. Non esiste una ricorsione esplicita, si evita la concatenazione e si usa invece ':', ed è un 1-liner. – bheklilr

+1

Puoi anche ottenere una lista intermedia con 'piecesOf n = unfoldr (Just. SplitAt n)' quindi 'concatMap' attraverso di essa con qualsiasi cosa. Probabilmente non è così breve ed efficiente come altri metodi, ma a volte può essere conveniente. –

+0

@ n.m. 'piecesOf' crea gruppi di' n', seguiti da un gruppo che può essere inferiore a 'n', seguito da un numero infinito di gruppi vuoti. – pat

risposta

2

Come Lei e Bheklilr menzionano, cycle fornisce un bel modo per realizzare questo. È possibile usufruire di pigrizia un po 'però:

mapEvery :: Int -> (a -> a) -> [a] -> [a] 
mapEvery n f = zipWith ($) (drop 1 . cycle . take n $ f : repeat id) 

L'uso di zipWith e cycle sembra più idiomatica (comportamento complesso composto da comportamenti più semplici) di una funzione ricorsiva scritta a mano che combina entrambe le attività.

Note: tail qui rende questa funzione non definito per n = 0 così drop 1 è preferibile.

Non esiste una funzione di libreria per questo di cui sono a conoscenza.

+2

Fare qualcosa per ogni 0 ° elemento di una lista è probabilmente assurdo, quindi è meglio che la funzione non funzioni in quel caso.Se capita che abbia senso in un particolare contesto, dovresti capire quale sia il comportamento appropriato per n = 0, non rendere la funzione totale in qualche modo arbitrario nel nome di "sicurezza". Fortunatamente il ciclo [] è già un errore, quindi cambiare coda per rilasciare 1 non ha alcun effetto. –

+0

@ReidBarton Buon punto –

+0

Bel lavoro passando da 'replicate' a' take'. Penso che sia più pulito, userò questo. Grazie. –

Problemi correlati