2010-06-23 38 views
46

bien, esto probablemente va a estar en el preludio, pero: ¿hay una función de biblioteca estándar para encontrar los elementos únicos en una lista? mi (re) aplicación, aclaraciones, es:elementos únicos en una lista de haskell

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 
+10

Su 'has' también es estándar; es solo 'flip elem'. – Nefrubyr

+3

O incluso 'tiene xs = (\' elem \ 'xs)'. – yatima2975

+0

@ yatima2975 ¿por qué estás usando 'elem' como infijo? – dopatraman

Respuesta

45

La función nub de Data.List (no, en realidad no está en el Preludio) definitivamente hace algo como lo que quiere, pero no es lo mismo que su función unique. Ambos conservan el orden original de los elementos, pero unique conserva la última ocurrencia de cada elemento, mientras que nub conserva la primera aparición.

Usted puede hacer esto para hacer nub actúan exactamente igual que unique, si eso es importante (aunque no tengo la sensación de que no lo es):

unique = reverse . nub . reverse 

Además, nub sólo es bueno para las listas pequeñas. Su complejidad es cuadrática, por lo que comienza a ser lenta si su lista puede contener cientos de elementos.

Si limita sus tipos a tipos que tienen una instancia de Ord, puede hacer que escale mejor. Esta variación en nub aún conserva el orden de los elementos de la lista, pero su complejidad es 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 _ _    = [] 

De hecho, ha sido proposed añadir nubOrd a Data.Set.

+1

Podría decirse que es mejor simplemente dejarlo como un conjunto en lugar de usar una lista en primer lugar – alternative

+0

Seamos honestos: 'nub' no es bueno para ningún lista. Incluso en la lista con 2 elementos 'nubOrd' es [más rápido] (https://github.com/nh2/haskell-ordnub#dont-use-nub). – nh2

+0

Esto es como un "tamiz de mapa" similar al "tamiz hash" impuro. – CMCDragonkai

88

Busqué (Eq a) => [a] -> [a] en Hoogle.

Primer resultado fue nub (eliminar elementos duplicados de una lista).

Hoogle es increíble.

+1

Además, puede proporcionar su propia función de igualdad como esta: nubBy :: (a -> a -> Bool) -> [a] -> [a] –

+0

Y si Bart alguna vez tiene tiempo, podríamos ver un nubOrd, que será más razonable en cuanto a rendimiento. –

+2

Vale la pena decir que la función 'nub' es del paquete' Data.List'. –

4

Creo que unique debe devolver una lista de elementos que solo aparecen una vez en la lista original; es decir, cualquier elemento de la lista original que aparezca más de una vez no debe incluirse en el resultado.

puedo sugerir una definición alternativa, 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) 

Éstos son algunos ejemplos que ponen de relieve las diferencias entre unique_alt y 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] 
+0

Esa es, de hecho, la definición de Data.List.Unique (única), aunque personalmente, nunca he corrido para ese caso de uso, mientras que la función "squash enumera para contener solo uno de los duplicados" es pan y mantequilla de muchos operaciones. –

8
import Data.Set (toList, fromList) 
uniquify lst = toList $ fromList lst 
+24

'uniquify = toList. fromList' – muhmuhten

+1

Esto cambia el orden de los elementos. – sjakobi

-1

Otra manera de eliminar los duplicados:

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

Algoritmo en Haskell para crear una lista única:

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) 

Salida:

Foo {id_ = 9, name_ = "Name"} 
Foo {id_ = 9, name_ = "Gaby"} 
Foo {id_ = 5, name_ = "Karl"} 
Foo {id_ = 8, name_ = "Tim"} 
Cuestiones relacionadas