web-dev-qa-db-fra.com

éléments uniques dans une liste de haskell

d'accord, cela va probablement être dans le prélude, mais: existe-t-il une fonction de bibliothèque standard pour trouver les éléments uniques dans une liste? ma (re) mise en œuvre, pour clarification, est:

has :: (Eq a) => [a] -> a -> Bool
has [] _ = False
has (x:xs) a
  | x == a    = True
  | otherwise = has xs a

unique :: (Eq a) => [a] -> [a]
unique [] = []
unique (x:xs)
  | has xs x  = unique xs
  | otherwise = x : unique xs
48
muhmuhten

La fonction nub de Data.List (non, ce n'est pas dans le prélude) fait certainement quelque chose comme ce que vous voulez, mais ce n'est pas tout à fait la même chose que votre fonction unique. Ils conservent tous les deux l'ordre d'origine des éléments, mais unique conserve la dernière occurrence De chaque élément, tandis que nub conserve la première occurrence.

Vous pouvez faire cela pour que nub agisse exactement comme unique, si c'est important (même si j'ai l'impression que ce n'est pas le cas):

unique = reverse . nub . reverse

De plus, nub n'est utile que pour les petites listes . Sa complexité est quadratique, elle commence donc à devenir lente si votre liste peut contenir des centaines d'éléments.

Si vous limitez vos types aux types ayant une instance Ord, vous pouvez la rendre plus évolutive . Cette variation sur nub conserve toujours l'ordre des éléments de la liste, mais sa complexité est O(n * log n):

import qualified Data.Set as Set

nubOrd :: Ord a => [a] -> [a] 
nubOrd xs = go Set.empty xs where
  go s (x:xs)
   | x `Set.member` s = go s xs
   | otherwise        = x : go (Set.insert x s) xs
  go _ _              = []

En fait, il a été proposé d'ajouter nubOrd à Data.Set.

49
Yitz

J'ai cherché (Eq a) => [a] -> [a] sur Google .

Le premier résultat était nub (supprime les éléments en double d'une liste).

Hoogle est génial.

90
Artelius
import Data.Set (toList, fromList)
uniquify lst = toList $ fromList lst
9
dpatru

Je pense que cet unique devrait renvoyer une liste d'éléments qui n'apparaissent qu'une fois dans la liste d'origine; c'est-à-dire que tous les éléments de la liste originale qui apparaissent plus d'une fois ne doivent pas être inclus dans le résultat.

Puis-je suggérer une autre définition, unique_alt:

    unique_alt :: [Int] -> [Int]
    unique_alt [] = []
    unique_alt (x:xs)
        | elem x ( unique_alt xs ) = [ y | y <- ( unique_alt xs ), y /= x ]
        | otherwise                = x : ( unique_alt xs )

Voici quelques exemples qui soulignent les différences entre unique_alt et unqiue:

    unique     [1,2,1]          = [2,1]
    unique_alt [1,2,1]          = [2]

    unique     [1,2,1,2]        = [1,2]
    unique_alt [1,2,1,2]        = []

    unique     [4,2,1,3,2,3]    = [4,1,2,3]
    unique_alt [4,2,1,3,2,3]    = [4,1]
4
Adam Grant

Je pense que cela le ferait.

unique [] = []
unique (x:xs) = x:unique (filter ((/=) x) xs)
1
Craig Norton

Une autre façon de supprimer les doublons:

unique :: [Int] -> [Int]
unique xs = [x | (x,y) <- Zip xs [0..], x `notElem` (take y xs)]
0
Juan Kujarchi

Algorithme dans Haskell pour créer une liste unique:

data Foo = Foo { id_ :: Int
               , name_ :: String
               } deriving (Show)

alldata = [ Foo 1 "Name"
          , Foo 2 "Name"
          , Foo 3 "Karl"
          , Foo 4 "Karl"
          , Foo 5 "Karl"
          , Foo 7 "Tim"
          , Foo 8 "Tim"
          , Foo 9 "Gaby"
          , Foo 9 "Name"
          ]

isolate :: [Foo] -> [Foo]
isolate [] = []
isolate (x:xs) = (fst f) : isolate (snd f)
  where
    f = foldl helper (x,[]) xs
    helper (a,b) y = if name_ x == name_ y
                     then if id_ x >= id_ y
                          then (x,b)
                          else (y,b)
                     else (a,y:b)

main :: IO ()
main = mapM_ (putStrLn . show) (isolate alldata)

Sortie:

Foo {id_ = 9, name_ = "Name"}
Foo {id_ = 9, name_ = "Gaby"}
Foo {id_ = 5, name_ = "Karl"}
Foo {id_ = 8, name_ = "Tim"}
0
Manuel