【问题标题】:Haskell: Removing duplicates tuples from a list?Haskell:从列表中删除重复的元组?
【发布时间】:2015-09-21 01:20:19
【问题描述】:

我正在尝试从之前状态到之后状态。有没有方便的 Haskell 函数从列表中删除重复的元组?或者它可能更复杂一些,比如遍历整个列表?

Before: the list of tuples, sorted by word, as in
   [(2,"a"), (1,"a"), (1,"b"), (1,"b"), (1,"c"), (2,"dd")]
After: the list of sorted tuples with exact duplicates removed, as in
   [(2,"a"), (1,"a"), (1,"b"), (1,"c"), (2,"dd")]

【问题讨论】:

  • map head . groupO(n) 时间内从 排序 列表中删除重复项。
  • 如果你允许...(1,"b"),(2,"b"),(1,"b"),...,那么我们就必须使用map (head . nub) . groupBy ((==) `on` snd)。大概最长的组仍然很短,所以nub 不会是这样的问题。如果没有,总是有SetHashMap。不过,首先在 O(n) 中将其分成几组,不会有什么坏处。 -- 另一种可能性是通过sndmajor 和fstminor 按字典顺序(使用stable sort)重新排序,这应该是接近线性的;然后通过map head . group.

标签: list haskell tuples


【解决方案1】:

hoogle上搜索Eq a => [a] -> [a],返回nub函数:

nub 函数从列表中删除重复元素。特别是,它只保留每个元素的第一次出现。 (名字 nub 的意思是“本质”。)

在文档中,更一般的情况是nubBy

也就是说,这是一个O(n^2) 算法,可能效率不高。如果值是 Ord 类型类的实例,另一种方法是使用 Data.Set.fromList,如下所示:

import qualified Data.Set as Set

nub' :: Ord a => [a] -> [a]
nub' = Set.toList . Set.fromList

尽管这不会保持原始列表的顺序。

保持原始列表顺序的简单设置样式解决方案可以是:

import Data.Set (Set, member, insert, empty)

nub' :: Ord a => [a] -> [a]
nub' = reverse . fst . foldl loop ([], empty)
    where
    loop :: Ord a => ([a], Set a) -> a -> ([a], Set a)
    loop acc@(xs, obs) x
        | x `member` obs = acc
        | otherwise = (x:xs, x `insert` obs)

【讨论】:

  • @shamu11, nub 对于不短的列表来说非常慢。如果元素可以排序,你可以做得更好。
  • reverse . foldl!这绝对是一个foldr 问题,很好而且流畅。
  • @behzad.nouri,在这种情况下,foldr 将比foldl 高效得多,甚至可以在运行foldl 内存不足的情况下成功完成。
  • @dfeuer 请做一些 基准测试 并表明 foldr 版本将“效率更高”。请注意,haskell nub 函数保持每个元素的 first 出现。它的替代实现也应该如此。
  • 看我的回答。我已经花了足够多的时间对列表代码进行基准测试,而不需要对这个特定的练习进行基准测试。
【解决方案2】:

如果你想为Ord定义一个nub的版本,我推荐使用

nub' :: Ord a => [a] -> [a]
nub' xs = foldr go (`seq` []) xs empty
  where
    go x r obs
      | x `member` obs = r obs
      | otherwise = obs' `seq` x : r obs'
      where obs' = x `insert` obs

要查看这是在做什么,您可以摆脱foldr

nub' :: Ord a => [a] -> [a]
nub' xs = nub'' xs empty
  where
    nub'' [] obs = obs `seq` []
    nub'' (y : ys) obs
      | y `member` obs = nub'' ys obs
      | otherwise = obs' `seq` y : nub'' ys obs'
      where obs' = y `insert` obs

关于这个实现的一个关键点,而不是 behzad.nouri 的优点在于它会在消耗元素时懒惰地产生元素。这对于缓存利用率和垃圾收集通常要好得多,并且使用比逆向算法更少的内存。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-17
    • 2018-12-25
    • 2017-04-22
    • 1970-01-01
    相关资源
    最近更新 更多