我在创建类似于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
问题是该功能不会删除重复的元素,除非它们在列表中在一起。
答案 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"]]