Vous pouvez transformer reverse . take n . reverse
en traitant votre liste comme un nombre naturel paresseux particulièrement obtus : les listes vides sont nulles, et les conses sont succés. Pour les nombres naturels paresseux codés comme des listes, la soustraction est drop
:
type LazyNat a = [a]
lengthLazy :: [a] -> LazyNat a
lengthLazy = id
dropLazy :: LazyNat a -> [b] -> [b]
dropLazy [] xs = xs
dropLazy (_:n) (_:xs) = dropLazy n xs
dropLazy _ _ = []
-- like Prelude.subtract, this is flip (-)
subtractLazy :: Int -> LazyNat a -> LazyNat a
subtractLazy = drop
Maintenant, nous pouvons facilement mettre en œuvre le "prendre le dernier n
fonction " :
takeLast n xs = dropLazy (subtractLazy n (lengthLazy xs)) xs
...et vous serez heureux d'apprendre que seulement n
conses doivent être en mémoire à un moment donné. En particulier, takeLast 1
(ou bien takeLast N
pour tout littéral N
) peut fonctionner en mémoire constante. Vous pouvez vérifier cela en comparant ce qui se passe lorsque vous exécutez takeLast 5 [1..]
avec ce qui se passe lorsque vous exécutez (reverse . take 5 . reverse) [1..]
en ghci.
Bien sûr, j'ai essayé d'utiliser des noms très suggestifs ci-dessus, mais dans une implémentation réelle, vous pourriez mettre en ligne toutes les absurdités ci-dessus :
takeLast n xs = go xs (drop n xs) where
go lastn [] = lastn
go (_:xs) (_:n) = go xs n
go _ _ = []