È 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?
risposta
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.
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. –
@ReidBarton Buon punto –
Bel lavoro passando da 'replicate' a' take'. Penso che sia più pulito, userò questo. Grazie. –
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
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. –
@ 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