Haskell函数nub效率低下

时间:2014-01-18 21:02:00

标签: performance algorithm haskell

我对Haskell标准库 Data.List 中'nub'(选择唯一值)函数的实现感到困惑。 GHC的实施是

nub l                   = nub' l []
  where
    nub' [] _           = []
    nub' (x:xs) ls
        | x `elem` ls   = nub' xs ls
        | otherwise     = x : nub' xs (x:ls)

据我所知,这有一个最坏情况下的时间复杂度为O(n ^ 2),因为对于一个唯一值列表,它必须比较它们一次才能看到它们实际上是唯一的。 / p>

如果使用哈希表,则复杂性可以减少到O(n)以构建表+ O(1)以检查每个值与哈希表中的先前值。当然,这不会产生有序列表,但如果有必要,也可以在O(n log n)中使用GHC自己的有序Data.Map。

为什么为重要的库函数选择这样低效的实现?我知道效率不是Haskell的主要关注点,但至少标准库可以努力为工作选择(渐近)最佳数据结构。

3 个答案:

答案 0 :(得分:10)

你是绝对正确的 - nub是一个O(n ^ 2)算法。但是,仍然有理由要使用它而不是使用hashmap:

  • 对于小型列表,它仍然可能更快
  • nub只需要Eq约束;相比之下,Data.Map要求对密钥设置Ord约束,Data.HashMap要求密钥类型同时包含HashableOrd类型
  • 它很懒 - 你不必遍历整个输入列表来开始获得结果

编辑:对第三点进行轻微修正 - 您无需处理整个列表即可开始获取结果;你仍然需要检查输入列表的每个元素(因此nub不能在无限列表上工作),但是一旦找到一个唯一的元素,你就会开始返回结果。

答案 1 :(得分:9)

在Haskell中,效率是一个非常值得关注的问题,毕竟语言与Java相当,并且在内存消耗方面胜过它,但当然不是C。

您的问题的答案非常简单:Prelude的nub仅需要Eq约束,而基于MapSet的任何实施也需要OrdHashable

答案 2 :(得分:4)

https://groups.google.com/forum/m/#!msg/haskell-cafe/4UJBbwVEacg/ieMzlWHUT_IJ

根据我的经验,“初学者”Haskell(包括Prelude和坏包)在很多情况下都会忽略性能,而不是简单。

Haskell性能是一个需要解决的复杂问题,因此如果您没有足够的经验来搜索平台或Hackage以寻找简单nub的替代方法(特别是如果您的输入位于List中仅仅是因为您没有不考虑替代结构),那么Data.List.nub可能不是你唯一的主要性能问题,而且你可能也在为性能无关紧要的玩具项目编写代码。

您必须相信,当您构建大型(代码或数据)项目时,您将更有经验并且知道如何更有效地设置程序。

换句话说,不要担心它,并假设来自Prelude或base的Haskell 98中的任何内容可能不是解决问题的最有效方法。