算法 - 如何删除Haskell列表中的重复元素

时间:2010-12-21 00:22:38

标签: haskell

我在创建类似于nub函数的函数时遇到问题。

我需要这个func来从列表中删除重复的元素。 当2个元素具有相同的电子邮件时,元素会重复,并且应该保留较新的元素(更接近列表的末尾)。

type Regist = [name,email,,...,date]
type ListRe = [Regist]

rmDup ListRe -> ListRe
rmDup [] = []
rmDup [a] = [a]
rmDup (h:t) | isDup h (head t) = rmDup t
            | otherwise = h : rmDup t

isDup :: Regist -> Regist -> Bool
isDup (a:b:c:xs) (d:e:f:ts) = b==e

问题是该功能不会删除重复的元素,除非它们在列表中在一起。

7 个答案:

答案 0 :(得分:12)

只需使用nubBy,并指定一个相等的函数,以您想要的方式比较事物。

如果你想保留最后一个元素而不是第一个元素,我想反过来几次。

答案 1 :(得分:5)

稍微修改原始代码版本以使其运行:

type Regist = [String]
type ListRe = [Regist]

rmDup :: ListRe -> ListRe
rmDup [] = []
rmDup (x:xs) = x : rmDup (filter (\y -> not(x == y)) xs)

结果:

*Main> rmDup [["a", "b"], ["a", "d"], ["a", "b"]]
[["a","b"],["a","d"]]

答案 2 :(得分:4)

Anon是正确的:nubBy是您正在寻找的功能,可以在Data.List中找到。

也就是说,你想要一个函数rem,它接受​​一个列表xs和一个函数f :: a -> a -> Bool(比较哪些元素从xs中删除)。由于定义是递归的,因此需要基本案例和递归案例。

在基本案例xs = []rem f xs = []中,因为从[]删除所有重复元素的结果是[]

rem :: Eq a => (a -> a -> Bool) -> [a] -> [a]
rem f [] = []

在递归案例中,xs = (a:as)。让as'成为通过从列表a'中删除f a a' = True所有元素as而获得的列表。这只是应用于列表filter (\a' -> not $ f a a')的函数as。他们rem f (a:as)是在rem f上递归调用as'的结果,即a : rem f as'

rem f (a:as) = a : rem f $ filter (\a' -> not $ f a a') as

替换f是一个比较列表元素的函数,以获得适当的相等性(电子邮件地址)。

答案 3 :(得分:3)

虽然nubBy有两个reverse可能是简单解决方案中最好的(而且可能正是Justin对他的任务所需要的),但是不应该忘记它不是理想的解决方案。效率条款 - 毕竟nubBy是O(n ^ 2)(在“最坏情况下” - 没有重复时)。两个reverse也会受到影响(以内存分配的形式)。 为了更有效地实现Data.Map(插入时为O(logN))可以用作中间“最新非复制元素”持有者(如果发生冲突,Set.insert用较新的元素替换旧元素):

import Data.List
import Data.Function
import qualified Data.Set as S

newtype Regis i e = Regis { toTuple :: (i,[e]) }

selector (Regis (_,(_:a:_))) = a 

instance Eq e => Eq (Regis i e) where
    (==) = (==) `on` selector

instance Ord e => Ord (Regis i e) where
    compare = compare `on` selector

rmSet xs = map snd . sortBy (compare `on` fst) . map toTuple . S.toList $ set
    where
      set = foldl' (flip (S.insert . Regis)) S.empty (zip [1..] xs)

虽然nubBy实施肯定要简单得多:

rmNub xs = reverse . nubBy ((==) `on` (!!1)) . reverse $ xs

在10M元素列表中(有很多重复 - nub在这里应该很好用)在运行时间方面有3倍的差异,在内存使用方面有700倍的差异。使用GHC编译-O2:

input = take 10000000 $ map (take 10) $ permutations [1..]

test1 = rmNub input
test2 = rmSet input

虽然不确定作者数据的性质(真实数据可能会改变图片)。

答案 4 :(得分:2)

(假设您想找出答案,而不仅仅是调用为您完成此项工作的库函数。)

你得到了你所要求的。如果h不等于head t但是等于t的第3个元素,该怎么办?您需要编写一个算法,将h t的每个元素进行比较,而不仅仅是第一个元素。

答案 5 :(得分:0)

为什么不将所有内容都放在从电子邮件到注册表的地图中(当然尊重您的“保持最新”规则),然后将列表中的值转换回列表中?这是我能想到的最有效的方式。

答案 6 :(得分:0)

我使用了Alexei Polkhanov's回答并且来到了以下内容,因此您可以从扩展Eq类的类型的列表中删除重复项。

removeDuplicates :: Eq a => [[a]] -> [[a]]
removeDuplicates [] = []
removeDuplicates (x:xs) = x : removeDuplicates (filter (\y -> not (x == y)) xs)

示例:

*Verdieping> removeDuplicates [[1],[2],[1],[1,2],[1,2]]
[[1],[2],[1,2]]

*Verdieping> removeDuplicates [["a","b"],["a"],["a","b"],["c"],["c"]]
[["a","b"],["a"],["c"]]