从Haskell中的列表中删除重复的元素

时间:2011-11-22 12:59:11

标签: haskell

我是Haskell的初学者。我只是想知道如何实现一个从数组中删除repeat元素的函数。例如,[1,1,1,3,4,2,2,3],结果应为[1,3,4,2]。我不想使用像元素这样的现有函数,并通过使用递归来实现它。我的想法是比较x:xs,如果x是重复元素,则执行递归,否则重新运行该函数。这是正确的,如何通过代码实现这一点?

5 个答案:

答案 0 :(得分:8)

如果您不能假设元素之间有任何顺序(即您不知道它是否是Ord的实例),那么您必须使用nub,就像一张海报已经提到的那样。不幸的是,这是O(n ^ 2)。

如果你的元素实现了Ord,那么你可以在O(nlog(n))中对列表进行排序,然后递归地删除相邻元素(这只会将O(n)添加到整个运行时)。像这样:

remove_dups :: (Ord a, Eq a) => [a] -> [a]
remove_dups xs = remove $ sort xs
  where
    remove []  = []
    remove [x] = [x]
    remove (x1:x2:xs)
      | x1 == x2  = remove (x1:xs)
      | otherwise = x1 : remove (x2:xs)

非常有趣的问题。我们经常需要这样做。 =)

修改

我没有注意到你给出的结果不是非递减顺序。上面的代码会生成[1,2,3,4],而这可能不是您想要的。

答案 1 :(得分:3)

您可以查看Haskell提供的nub函数。

http://www.haskell.org/onlinereport/list.html

这是代码:

nub                     :: (Eq a) => [a] -> [a]
nub                      = nubBy (==)

nubBy                   :: (a -> a -> Bool) -> [a] -> [a]
nubBy eq []              = []
nubBy eq (x:xs)          = x : nubBy eq (filter (y -> not (eq x y)) xs)

实际上,我找到了一个网页,它向您展示了比Haskell提供的更有效的实现: http://buffered.io/posts/a-better-nub/

答案 2 :(得分:1)

您正走在正确的轨道上:您正在考虑列表的头部(示例中为x)和尾部(xs)。如你所知,你需要做两件事:

  1. 编写一个功能,删除xxs的所有重复项。
  2. 对待列表的其余部分...现在xxs的任何重复内容都已消失,您如何处理?在纸上手动尝试,观察你的大脑做了什么。
  3. 首先尝试解决第一个任务,并确保您编写的函数适用于某些测试用例(在GHCi中试用,或者直到您对其工作满意为止)。

    对于第二项任务,当你手动解决这个问题时,它会再次尝试观察你的大脑是如何处理的。

答案 3 :(得分:0)

这个问题有很多解决方案,所需要的可能取决于你的课程到目前为止所涵盖的内容。

我会观察到“存在”函数通常具有对数运行时间,具体取决于所使用的数据结构,并且构建最好的数据结构需要在最差的“n log n”时间。

如果这听起来像胡言乱语,请不要担心。您将学习算法或复杂性理论课程中的运行时间。我只是说设计良好的存在功能比你意识到的要快得多。

顺便说一句,有一种称为哈希函数的东西可以让你为更大的数组做更细粒度的时空权衡,但这超出了你当前课程的范围。

答案 4 :(得分:0)

我也在学习Haskell,我写了这个Code.I认为它解决了你的问题。我这是我能想到的最简单的方式。

takeRep :: Eq a => [a] -> [a]  
takeRep [] = []
takeRep (x:xs)
    | checkIt x xs = takeRep xs
    | otherwise = x:takeRep xs

checkIt :: Eq a => a -> [a] -> Bool
checkit _ [] = False
checkIt n (x:xs)
    | n == x = True
    | otherwise = checkIt n xs