Haskell中是否有一个函数会接收任何数据类型的列表(特别是String
),并检查列表中是否有重复的元素,即String
中的重复字母?
答案 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
一个很好的启发式方法,可以检查某些事情是否表现得很懒散 - 也就是说,它是否会在尽可能少地评估时给出答案 - 就是看它在无限列表上的效果如何。我们不能指望任何hasDup
在hasDup [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
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