在没有elem的情况下从Haskell中的列表中删除重复项

时间:2013-04-19 15:56:51

标签: list haskell recursion

我正在尝试定义一个从列表中删除重复项的函数。到目前为止,我有一个有效的实施方案:

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs)   | x `elem` xs   = rmdups xs
                | otherwise     = x : rmdups xs

但是我想在不使用elem的情况下重做这件事。对此最好的方法是什么?

我想使用自己的功能,而不是nubnubBy

12 个答案:

答案 0 :(得分:53)

您的代码和nub都有O(N^2)复杂性。

您可以将复杂性提高到O(N log N),并通过排序,分组和仅采用每个组的第一个元素来避免使用elem

从概念上讲,

rmdups :: (Ord a) => [a] -> [a]
rmdups = map head . group . sort

假设您从列表[1, 2, 1, 3, 2, 4]开始。通过排序,你得到[1, 1, 2, 2, 3, 4];通过分组,你得到[[1, 1], [2, 2], [3], [4]];最后,通过取每个列表的头部,你得到[1, 2, 3, 4]

上述内容的全面实施只涉及扩展每项功能。

请注意,这需要对列表元素进行更强的Ord约束,并且还会在返回的列表中更改它们的顺序。

答案 1 :(得分:39)

更容易。

import Data.Set 
mkUniq :: Ord a => [a] -> [a]
mkUniq = toList . fromList

将集合转换为 O(n)时间内的元素列表:

toList :: Set a -> [a]

O(n log n)时间内的元素列表创建一个集合:

fromList :: Ord a => [a] -> Set a

在python中它也不例外。

def mkUniq(x): 
   return list(set(x)))

答案 2 :(得分:25)

与@ scvalex的解决方案相同,以下内容具有O(n * log n)复杂度和Ord依赖关系。与此不同的是,它保留了订单,保留了第一批项目。

import qualified Data.Set as Set

rmdups :: Ord a => [a] -> [a]
rmdups = rmdups' Set.empty where
  rmdups' _ [] = []
  rmdups' a (b : c) = if Set.member b a
    then rmdups' a c
    else b : rmdups' (Set.insert b a) c

基准测试结果

benchmark results

如您所见,基准测试结果证明此解决方案最有效。 您可以找到此基准here的来源。

答案 3 :(得分:21)

如果没有elem(或者您自己重新实施),我认为您无法做到这一点。

但是,您的实现存在语义问题。当元素重复时,你将保留最后一个。就个人而言,我希望它保留第一个重复的项目,然后放弃其余项目。

*Main> rmdups "abacd"
"bacd"

解决方案是将'see'元素作为状态变量进行处理。

removeDuplicates :: Eq a => [a] -> [a]
removeDuplicates = rdHelper []
    where rdHelper seen [] = seen
          rdHelper seen (x:xs)
              | x `elem` seen = rdHelper seen xs
              | otherwise = rdHelper (seen ++ [x]) xs

这或多或少是nub在标准库中的实现方式(阅读源here)。 nub实现中的微小差异确保它是non-strict,而上面的removeDuplicates是严格的(它在返回之前会占用整个列表)。

如果你不担心严格,原始递归在这里实际上是过度的。可以使用removeDuplicates

在一行中实现foldl
removeDuplicates2 = foldl (\seen x -> if x `elem` seen
                                      then seen
                                      else seen ++ [x]) []

答案 4 :(得分:1)

使用recursion-schemes

import Data.Functor.Foldable

dedup :: (Eq a) => [a] -> [a]
dedup = para pseudoalgebra
    where pseudoalgebra Nil                 = []
          pseudoalgebra (Cons x (past, xs)) = if x `elem` past then xs else x:xs

虽然这肯定更先进,但我认为它非常优雅,并展示了一些有价值的函数式编程范例。

答案 5 :(得分:1)

现在回答这个问题为时已晚,但我希望在不使用elem的情况下分享我的原创解决方案,并且不要假设Ord

rmdups' :: (Eq a) => [a] -> [a]
rmdups' [] = []
rmdups' [x] = [x]
rmdups' (x:xs) = x : [ k  | k <- rmdups'(xs), k /=x ]

此解决方案在输入结束时删除重复项,而问题实现在开头删除。例如,

rmdups "maximum-minimum"
-- "ax-nium"

rmdups' "maximum-minimum"
-- ""maxiu-n"

此外,此代码复杂度为O(N * K),其中N是字符串的长度,K是字符串中唯一字符的数量。 N> = K因此,在最坏的情况下它将是O(N ^ 2),但这意味着字符串中没有重复,这与您尝试删除字符串中的重复项不同。

答案 6 :(得分:1)

您也可以使用此压缩功能。

cmprs ::Eq a=>[a] -> [a]
--cmprs [] = [] --not necessary
cmprs (a:as) 
    |length as == 1 = as
    |a == (head as) = cmprs as
    |otherwise = [a]++cmprs as

答案 7 :(得分:0)

Graham Hutton 在p上有rmdups个函数。 在Haskell编程中的86。它保留了秩序。它如下。

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : filter (/= x) (rmdups xs)
rmdups "maximum-minimum"
  

&#34; maxiu正&#34;

在看到赫顿的功能之前,这一直困扰着我。然后,我再次尝试。有两个版本,第一个保留最后一个副本,第二个保留第一个。

rmdups ls = [d|(z,d)<- zip [0..] ls, notElem d $ take z ls]
rmdups "maximum-minimum"
  

&#34; maxiu正&#34;

如果您想要获取列表中的第一个而不是最后一个重复元素,就像您尝试的那样,只需将take更改为函数中的drop并更改枚举{{1转到zip [0..]

答案 8 :(得分:0)

使用dropWhile也可以,但是请记住在使用此功能之前对列表进行排序

rmdups :: (Eq a) => [a] -> [a]
rmdups [] = []
rmdups (x:xs) = x : (rmdups $ dropWhile (\y -> y == x) xs)

答案 9 :(得分:0)

remove_duplicates (x:xs) | xs == [] = [x] | x == head (xs) = remove_duplicates xs | otherwise = x: remove_duplicates xs

您可以尝试执行此操作。我只是用自己的实现替换了“ elem”。它对我有用。

答案 10 :(得分:0)

我想在@fp_mora 的回答中补充说,在 Haskell 编程第 136 页 上还有另一个稍微不同的实现:

rmdups :: Eq a => [a] -> [a]
rmdups [] = []
rmdups (x : xs) = x : rmdups (filter (/= x) xs)

我更容易围绕这一点。

答案 11 :(得分:-1)

...或者使用应用于自身的Data.List中的函数union:

@Entity
@Table(name = "office")
public class Office extends AppModel{
    @Id
    public Long id;

    @Constraints.Required(message = "asdfasfd")
    public String name;
...
}