Comment aplatir une liste imbriquée comme ceci:
[1, 2, 3, 4] == flatten [[[1,2],[3]],[[4]]]
Puisque personne d'autre ne l'a donné, il est possible de définir une fonction qui aplatira les listes d'une profondeur arbitraire en utilisant MultiParamTypeClasses. Je ne l'ai pas vraiment trouvé utile, mais j'espère que cela pourrait être considéré comme un hack intéressant. J'ai eu l'idée de l'implémentation de la fonction polyvariade d'Oleg.
{-# LANGUAGE MultiParamTypeClasses, OverlappingInstances, FlexibleInstances #-}
module Flatten where
class Flatten i o where
flatten :: [i] -> [o]
instance Flatten a a where
flatten = id
instance Flatten i o => Flatten [i] o where
flatten = concatMap flatten
Maintenant, si vous le chargez et exécutez dans ghci:
*Flatten> let g = [1..5]
*Flatten> flatten g :: [Integer]
[1,2,3,4,5]
*Flatten> let h = [[1,2,3],[4,5]]
*Flatten> flatten h :: [Integer]
[1,2,3,4,5]
*Flatten> let i = [[[1,2],[3]],[],[[4,5],[6]]]
*Flatten> :t i
i :: [[[Integer]]]
*Flatten> flatten i :: [Integer]
[1,2,3,4,5,6]
Notez qu'il est généralement nécessaire de fournir l'annotation du type de résultat, car sinon ghc ne peut pas savoir où arrêter récursivement l'application de la méthode de classe flatten
. Si vous utilisez une fonction avec un type monomorphe, cela suffit cependant.
*Flatten> :t sum
sum :: Num a => [a] -> a
*Flatten> sum $ flatten g
<interactive>:1:7:
No instance for (Flatten Integer a0)
arising from a use of `flatten'
Possible fix: add an instance declaration for (Flatten Integer a0)
In the second argument of `($)', namely `flatten g'
In the expression: sum $ flatten g
In an equation for `it': it = sum $ flatten g
*Flatten> let sumInt = sum :: [Integer] -> Integer
*Flatten> sumInt $ flatten g
15
*Flatten> sumInt $ flatten h
15
Oui, c'est concat
du Prélude Standard, donné par
concat :: [[a]] -> [a]
concat xss = foldr (++) [] xss
Si vous souhaitez activer [[[a]]]
en [a]
, vous devez l'utiliser deux fois:
Prelude> (concat . concat) [[[1,2],[3]],[[4]]]
[1,2,3,4]
Comme d'autres l'ont souligné, concat :: [[a]] -> [a]
Est la fonction que vous recherchez, et elle ne peut pas aplatir des listes imbriquées de profondeur arbitraire. Vous devez l'appeler plusieurs fois pour l'aplatir au niveau souhaité.
L'opération se généralise cependant à d'autres monades. Il est alors appelé join
et a le type Monad m => m (m a) -> m a
.
Prelude Control.Monad> join [[1, 2], [3, 4]]
[1,2,3,4]
Prelude Control.Monad> join (Just (Just 3))
Just 3
Prelude Control.Monad.Reader> join (+) 21
42
import Data.List
let flatten = intercalate []
flatten $ flatten [[[1,2],[3]],[[4]]]
[1,2,3,4]
Comme l'a souligné Hammar, join
est le moyen "monadique" d'aplatir une liste. Vous pouvez également utiliser la notation do
- pour écrire des fonctions d'aplatissement de plusieurs niveaux:
flatten xsss = do xss <- xsss
xs <- xss
x <- xs
return x
Une liste imbriquée arbitrairement peut être approximée par un Data.Tree
, Qui peut être aplati par la fonction nommée de manière appropriée flatten
.
Je dis approximé parce que Data.Tree
Permet à un élément de données d'être attaché à chaque nœud, pas seulement aux feuilles. Cependant, vous pouvez créer une Data.Tree (Maybe a)
, et attacher Nothing
aux nœuds du corps, et aplatir avec catMaybes . flatten
.
Vous pouvez supprimer un niveau d'imbrication à l'aide de concat
et, par conséquent, vous pouvez appliquer n niveaux d'imbrication en appliquant concat
n fois.
Il n'est pas possible d'écrire une fonction qui supprime un niveau arbitraire d'imbrication, car il n'est pas possible d'exprimer le type d'une fonction, qui prend une liste imbriquée arbitrairement et renvoie une liste plate, en utilisant le système de type de Haskell (en utilisant le type de données de liste c'est-à-dire que vous pouvez écrire votre propre type de données pour des listes imbriquées arbitrairement et écrire une fonction d'aplatissement pour cela).