确定Haskell中任何数据类型列表中是否存在重复项

时间:2013-10-07 16:57:16

标签: haskell

Haskell中是否有一个函数会接收任何数据类型的列表(特别是String),并检查列表中是否有重复的元素,即String中的重复字母?

4 个答案:

答案 0 :(得分:8)

Data.List模块中的nub功能

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

删除已在列表中看到的重复项,否则保留订单。

ghci> nub [1,3,5,3,6,5]
[1,3,5,6]

您可以使用此功能将您正在寻找的功能编写为简单的单行。由于懒惰,它只有在使用Eq约束时才能获得效率! (如果你需要Ord,你可以做得更好)

答案 1 :(得分:4)

这是一种更先进的治疗方法。到目前为止所有的答案(包括我的另一个)在考虑到懒惰时都没有达到最佳效果。他们在这里:

import Data.List (nub, group, sort)

hasDup_nub, hasDup_any :: (Eq a)  => [a] -> Bool
hasDup_sort            :: (Ord a) => [a] -> Bool

hasDup_nub xs = nub xs /= xs

hasDup_any [] = False
hasDup_any (x:xs) = any (x ==) xs || hasDup_any xs

hasDup_sort = any ((> 1) . length) . group . sort

一个很好的启发式方法,可以检查某些事情是否表现得很懒散 - 也就是说,它是否会在尽可能少地评估时给出答案 - 就是看它在无限列表上的效果如何。我们不能指望任何hasDuphasDup [1,2..]上给出答案,因为没有重复发生,但我们从不评估整个列表,因此无法判断将来是否会有一个。但是,hasDup [1,1..]肯定会返回True - 前两个元素甚至是相同的。让我们看看他们的表现如何:

>>> hasDup_nub [1,1..]
^CInterrupted.
>>> hasDup_any [1,1..]
True
>>> hasDup_sort [1,1..]
^CInterrupted.

nub [1,1..][1(如果你允许我这样编写它 - 也就是说,如果你试图评估更多的元素,它就是一个无限循环),所以当检查列表是否相等,我们检查第二个元素是否为1,然后进入无限循环。 sort [1,1..]只是简单的旧底(另一种说无限循环的方式),所以我们永远不会从中得到任何信息。 hasDup_any没关系,因为它会检查第一个元素与列表的其余部分。但它很容易被挫败:

>>> hasDup_any (1:[2,2..])
^CInterrupted.

我们在尝试评估any (1 ==) [2,2..]时遇到困难,因为我们从未找到1,但我们不知道我们是否必须继续看得更远。

在懒惰的情况下,所有这三个都表现不同。这是hasDup_nub返回的时候,其他人没有:

>>> hasDup_nub (1:2:2:[3,3..])
True

那是因为nub (1:2:2:[3,3..]) = [1,2,3(在我的无限循环符号中),这足以说明它不等于[1,2,2,3,3,3..],因为它们在第三个元素中不同。

hasDup_sort是最严格的 - 当其他两个人没有回答时,它也不会。

然而,@ Satvik的答案指出hasNub_sort比其他两个更复杂。它的渐近复杂度是 O(n log n),而另外两个是 O(n ^ 2)

事实证明,hasNub_sort的渐近复杂度和比其他任何解决方案更好的严格性都有一个非常简单的解决方案。我们只是沿着列表选择,记录我们到目前为止看到的内容,如果我们遇到过我们已经看过的元素,我们就会回来。如果我们使用Data.Set来跟踪我们看到的内容,那么针对该集合的插入和测试是 O(log n)时间,并且我们到达 O( n log n)解决方案。

import qualified Data.Set as Set

hasDup_set :: (Ord a) => [a] -> Bool
hasDup_set = go Set.empty
    where
    go seen [] = False
    go seen (x:xs)
        | x `Set.member` seen = True
        | otherwise           = go (Set.insert x seen) xs

到目前为止,任何无限测试用例都没有问题:

>>> hasDup_set [1,1..]
True
>>> hasDup_set (1:[2,2..])
True
>>> hasDup_set (1:2:2:[3,3..])
True

当然,如果你给它一个没有重复的无限列表,它仍然无法分辨。没有可计算的功能(虽然仍然尊重参考透明度):

>>> hasDup_set [1,2..]
^CInterrupted.

我相信hasDup_set在渐近复杂性和懒惰方面都能做得很好*(但我肯定会接受挑战)。幸运的是,它们可以同时获得它们 - 有时候你必须做出必要的权衡。

***不使用unamb,这通常可以达到更高的懒惰程度。我没有讨论过unamb,因为这对我来说非常困难,而且我没有(也没有任何人,据我所知)想出一个有效使用它的好方法。 See here尝试,以及它可以获得多么复杂和微妙的例子。

答案 2 :(得分:2)

自己写作并不难。一个天真的解决方案

import Data.List

isRepeat :: Ord a => [a] -> Bool
isRepeat = any ((>1) . length) . group . sort

可以使用Eq编写解决方案(使用@luqui建议的nub),但这至少是O(n ^ 2)(我可能错了)。使用Ord约束使您可以灵活地以较低的复杂度执行此操作。

正如@luqui所指出的,上述解决方案并不足以在无限列表上工作。它是他指出的解决方案中最快的解决方案之一。似乎使用Set版本将为您提供最好的两个世界。

以下是基准测试结果。

warming up
estimating clock resolution...
mean is 1.639027 us (320001 iterations)
found 2846 outliers among 319999 samples (0.9%)
  1988 (0.6%) high severe
estimating cost of a clock call...
mean is 43.37523 ns (12 iterations)![enter image description here][1]
found 2 outliers among 12 samples (16.7%)
  1 (8.3%) high mild
  1 (8.3%) high severe

benchmarking Repeats in a list/Repeat with Sort
mean: 546.5002 us, lb 543.3960 us, ub 552.4869 us, ci 0.950
std dev: 21.33737 us, lb 12.23079 us, ub 35.02675 us, ci 0.950
found 17 outliers among 100 samples (17.0%)
  6 (6.0%) high mild
  11 (11.0%) high severe
variance introduced by outliers: 35.597%
variance is moderately inflated by outliers

benchmarking Repeats in a list/Repeat with set
mean: 585.5344 us, lb 582.7982 us, ub 589.2482 us, ci 0.950
std dev: 16.17934 us, lb 12.98695 us, ub 20.36634 us, ci 0.950
found 14 outliers among 100 samples (14.0%)
  10 (10.0%) high mild
  4 (4.0%) high severe
variance introduced by outliers: 21.917%
variance is moderately inflated by outliers

benchmarking Repeats in a list/Repeat with nub
mean: 10.24364 ms, lb 10.12385 ms, ub 10.40203 ms, ci 0.950
std dev: 700.2262 us, lb 561.3143 us, ub 851.5999 us, ci 0.950
found 17 outliers among 100 samples (17.0%)
  2 (2.0%) high mild
  15 (15.0%) high severe
variance introduced by outliers: 63.587%
variance is severely inflated by outliers

benchmarking Repeats in a list/Repeat with any
mean: 6.796171 ms, lb 6.712188 ms, ub 6.941347 ms, ci 0.950
std dev: 552.0155 us, lb 380.1686 us, ub 859.1697 us, ci 0.950
found 13 outliers among 100 samples (13.0%)
  4 (4.0%) high mild
  9 (9.0%) high severe
variance introduced by outliers: 71.738%
variance is severely inflated by outliers

enter image description here

x轴以ms为单位。

答案 3 :(得分:1)

当发现重复时,此解决方案将短路。为了完整起见,我在any中添加了(||)Prelude的定义。

import           Prelude hiding (any, (||))

x,y :: [Char]
x = "hello"
y = "helo"

main :: IO ()
main = mapM_ print [hasDupe x, hasDupe y]

hasDupe :: Eq a => [a] -> Bool
hasDupe [] = False
hasDupe (x:xs) = any (==x) xs || hasDupe xs

any :: (a -> Bool) -> [a] -> Bool
any f [] = False
any f (x:xs) = f x || any f xs

(||)                    :: Bool -> Bool -> Bool
True  || _              =  True
False || x              =  x