web-dev-qa-db-fra.com

Learning Haskell: Comment supprimer un élément d'une liste dans Haskell

Essayer d'apprendre Haskell. J'essaie d'écrire une fonction simple pour supprimer un numéro d'une liste sans utiliser la fonction intégrée (supprimer ... je pense). Par souci de simplicité, supposons que le paramètre d'entrée soit un entier et que la liste est une liste entière. Voici le code que j'ai, S'il vous plaît dites-moi ce qui ne va pas avec le code suivant

areTheySame :: Int -> Int-> [Int]

areTheySame x y | x == y = []
                | otherwise = [y]

removeItem :: Int -> [Int] -> [Int]

removeItem x (y:ys) = areTheySame x y : removeItem x ys
19
BM.

Les autres ont raison, le problème est l'opérateur :. Je dirais que votre fonction areTheySame qui retourne une liste est quand même une mauvaise approche. Plutôt que de passer à l'opérateur ++, une meilleure implémentation de cette fonction serait:

removeItem _ []                 = []
removeItem x (y:ys) | x == y    = removeItem x ys
                    | otherwise = y : removeItem x ys

Comme vous pouvez le constater, cette implémentation est assez simple. En outre, une telle consommation est beaucoup moins contraignante pour votre programme que l'ajout de plusieurs listes. Cela présente également d'autres avantages, tels que travailler paresseusement.

28
Chuck

L'opérateur : ne fait pas ce que vous pensez qu'il fait:

(:) :: a -> [a] -> [a]

Il prend un élément de type a et l'ajoute au début d'une liste de type a. Vous l'utilisez pour joindre deux listes de type a. Pour cela, vous devez utiliser ++:

(++) :: [a] -> [a] -> [a]

De plus, si vous créez une fonction récursive, elle nécessite une condition de fin. Alors essayez ceci:

removeItem _ [] = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys

De cette façon, lorsque vous arriverez à la fin de la liste, la fonction cessera de se répéter.

10
Chris Lutz

Vous pouvez aussi faire cela comme une liste de compréhension

delete :: Eq a => a -> [a] -> [a]
delete deleted xs = [ x | x <- xs, x /= deleted ]
9
Dagititis

Voici le correctif minimal pour que votre exemple fonctionne:

removeItem :: Int -> [Int] -> [Int]
removeItem _ []     = []
removeItem x (y:ys) = areTheySame x y ++ removeItem x ys

Tout d'abord, vous devez utiliser ++ pour concaténer des listes, car l'opérateur : que vous avez utilisé n'ajoute qu'un élément au début d'une liste (il ne peut pas être utilisé pour ajouter des listes avec un élément ni pour ajouter des listes vides). Commencez par comparer l'en-tête de la liste (y) à l'élément que vous souhaitez supprimer et renvoyez correctement l'élément ou une liste vide à l'aide de areTheySame. Ensuite, vous voulez continuer à utiliser récursivement removeItem sur le reste de la liste (ys). La liste résultante doit être concaténée à l'aide de ++.

Deuxièmement, comme l'a souligné Chris Lutz, vous avez besoin d'une condition de fin lorsque vous atteignez la fin de la liste. En ajoutant cette ligne, Haskell sait quoi faire avec une liste vide (c'est-à-dire rien, retourne juste une liste vide).

Comme Chuck l'a dit, vous pouvez simplifier le code pour cette tâche en faisant removeItem non pas déléguer la tâche de la comparaison, mais en le comparant et en jetant l'élément s'il devait être supprimé, sinon conservez-le en tête de liste (avec :). Dans tous les cas, continuez récursivement avec le reste de la liste.

-- nothing can be removed from an empty list
-- ==> return empty list and stop recursion
removeItem _ []     = []

-- if the list is not empty, cut off the head in y and keep the rest in ys
--                    if x==y, remove y and continue
removeItem x (y:ys) | x == y    = removeItem x ys    
--                    otherwise, add y back and continue 
                    | otherwise = y : removeItem x ys
3
ahans

Pour référence, vous voudrez peut-être voir comment faire dans delete à partir de Data.List .

Vous pouvez laisser areTheySame tel quel, mais vous devrez alors utiliser concatMap in removeItem pour réduire les listes vides:

removeItem :: Int -> [Int] -> [Int]
removeItem x xs = concatMap (areTheySame x) xs

ou équivalent

removeItem :: Int -> [Int] -> [Int]
removeItem x = concatMap (areTheySame x)

Notez que les types de vos fonctions pourraient être plus généraux:

areTheySame :: (Eq a) => a -> a -> [a]
removeItem  :: (Eq a) => a -> [a] -> [a]

Cela permet de supprimer des éléments de n'importe quel type de liste pour lequel == est défini, pas seulement Int.

3
Greg Bacon

J'ai écrit une fonction dans une seule ligne de code:

remove element list = filter (\e -> e/=element) list

Par exemple:

remove 5 [1..10]

[1,2,3,4,6,7,8,9,10]

remove 'b' ['a'..'f']

"acdef"

2

Je pense que toutes les solutions proposées jusqu'ici fonctionnent différemment de Data.List.delete, qui ne supprime que le premier membre.

deleteFromList x xs =
  case break (==x) xs of
    (_,[]) -> xs
    (notsat,sat) -> notsat ++ tail sat

était ma tentative de supprimer uniquement le premier membre (pas encore atteint D.L).

On ignore quel comportement la meilleure affiche veut.

0
tphyahoo