【问题标题】:Is there a function that takes a list and returns a list of duplicate elements in that list?是否有一个函数可以获取一个列表并返回该列表中重复元素的列表?
【发布时间】:2015-05-07 10:05:01
【问题描述】:

是否有一个 Haskell 函数接受一个列表并返回该列表中重复/冗余元素的列表?

我知道 nub 和 nubBy 函数,但它们删除重复项;我想保留这些骗子并将它们收集在一个列表中。

【问题讨论】:

    标签: list haskell functional-programming


    【解决方案1】:

    我编写了这些似乎运行良好的函数。

    第一个返回具有基本相等性测试的列表中的重复元素列表 (==)

    duplicate :: Eq a => [a] -> [a]
    duplicate  []             =  []
    duplicate  (x:xs)         
        | null pres = duplicate  abs
        | otherwise = x:pres++duplicate  abs
      where (pres,abs) = partition (x ==) xs
    

    第二个通过提供相等性测试功能(如nubBy)完成同样的工作

    duplicateBy :: (a -> a -> Bool) -> [a] -> [a]
    duplicateBy eq []             =  []
    duplicateBy eq (x:xs)         
        | null pres = duplicateBy eq abs
        | otherwise = x:pres++duplicateBy eq abs
      where (pres,abs) = partition (eq x) xs
    

    【讨论】:

    • 出于效率原因,您希望尽可能避免使用++;没必要,在这里。
    • @Jubobs,嗯,这不是准确地正确,但我没有分析这个特殊用途。
    • @dfeuer 在我的书中尽可能避免++ 是一个足够好的经验法则。
    • @Jubobs,由于列表融合,它在某些情况下确实非常好。但是您确实必须考虑何时适用(如果您担心,请检查核心)。此外,如果您确实需要将此列表粘贴到该列表上,那么使用 Prelude.++ 几乎肯定比自己滚动要好。
    • @dfeuer 实际上,我需要阅读更多关于 list fusion 的信息。感谢您指出。
    【解决方案2】:

    最简单但效率极低的方法是使用nub和\\:

    import Data.List (nub, (\\))
    
    getDups :: Eq a => [a] -> [a]
    getDups xs = xs \\ nub xs
    

    如果您可以接受Ord 约束,一切都会变得更好:

    import Data.Set (member, empty, insert)
    
    getDups :: Ord a => [a] -> [a]
    getDups xs = foldr go (const []) xs empty
      where
        go x cont seen
          | member x seen = x : r seen
          | otherwise = r (insert x seen)
    

    【讨论】:

      【解决方案3】:

      是否有一个 Haskell 函数接受一个列表并返回该列表中重复/冗余元素的列表?

      您可以很容易地自己编写这样的函数。使用一个辅助函数,它接受两个列表参数,第一个是要寻找其欺骗的列表;沿着该列表走,并在第二个参数中累积欺骗;最后,当第一个参数为空列表时,返回后者。

      dupes l                                    = dupes' l []
        where
          dupes' []     ls                       = ls
          dupes' (x:xs) ls
              | not (x `elem` ls) && x `elem` xs = dupes' xs (x:ls)
              | otherwise                        = dupes' xs ls
      

      测试:

      λ> dupes [1,2,3,3,2,2,3,4]
      [3,2]
      

      请注意,渐近时间复杂度与nub 一样糟糕,不过:O(n^2)。如果你想要更好的渐近线,你需要一个 Ord 类约束。

      【讨论】:

        【解决方案4】:

        如果您对 Ord 约束感到满意,您可以使用来自 Data.List 的 group:

        getDups :: Ord a => [a] -> [a]
        getDups = concatMap (drop 1) . group . sort
        

        【讨论】:

        • 如果重复元素不连续,这可能不起作用:getDups [1,2,1]
        • 如此初始排序。
        • 但是nub 只假设Eq,而不是Ord。
        • @Jubobs,但 Data.List.nub 因此对于几乎所有应用程序来说都是错误的函数。
        • @dfeuer 我无法反驳这一点,但 OP 的类型是否可订购尚不清楚。
        猜你喜欢
        • 2021-06-07
        • 2021-12-23
        • 1970-01-01
        • 2013-01-04
        • 2022-01-15
        • 2016-06-21
        • 1970-01-01
        • 2018-03-11
        • 2021-04-14
        相关资源
        最近更新 更多