【问题标题】:Removing repeated elements from a list in Haskell从 Haskell 中的列表中删除重复的元素
【发布时间】:2016-10-23 15:19:35
【问题描述】:

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

【问题讨论】:

  • 吹毛求疵:[1,1,1,3,4,2,2,3] 不是数组,而是链表。性能特征非常不同。
  • 其实,这是工作的一部分,我想不通。其他部分我已经完成了。

标签: haskell


【解决方案1】:

如果你不能假设元素之间有任何顺序(即你不知道它是否是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],这可能不是你想要的。

【讨论】:

  • 好吧,is7s,发帖人并没有说这是为了他/她的家庭作业。我回答认为不是。
【解决方案2】:

你可以看看 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/

【讨论】:

  • @augustss 我在它被标记为作业之前回答了。 已编辑 我找到了关于作业政策 (meta.stackexchange.com/questions/18242/…) 的部分,虽然它不是官方的,但我会记住它。另一方面,请注意,这是单个 google 搜索的结果,并且它只是 haskell 库提供的实现。换句话说,我没有向他展示任何他无法轻易找到的东西。
  • 如果性能很重要,那么也可以考虑nubOrd
【解决方案3】:

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

  1. 编写一个函数,删除xsx 的所有重复项。
  2. 处理列表的其余部分...现在xs 中的x 的任何重复项都已消失,您将如何进行?在纸上手动尝试一下,观察你的大脑在做什么。

尝试先解决第一个任务,并确保您编写的函数在某些测试用例上有效(在 GHCi 或其他东西中尝试,直到您满意它有效为止)。

对于第二个任务,当您手动解决此问题时,尝试观察您的大脑是如何进行的,这再次非常有帮助。

【讨论】:

    【解决方案4】:

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

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

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

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

    【讨论】:

      【解决方案5】:

      我也在学习 Haskell,我写了这段代码。我认为它可以解决你的问题。我用我能想到的最简单的方法做到了。

      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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-01-01
        • 1970-01-01
        • 2015-01-05
        • 2012-05-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多