我需要编写一个函数,该函数检查列表中是否包含两个或更多相同的元素并返回true或false。
例如[3,3,6,1]
应该返回true,但是[3,8]
应该返回false。
这是我的代码:
identical :: [Int] -> Bool
identical x = (\n-> filter (>= 2) n )( group x )
我知道这很糟糕,并且不起作用。 我想将列表分为列表列表,如果列表的长度> = 2,则返回true,否则返回false。
答案 0 :(得分:6)
使用any
获得布尔值。
any ( . . . ) ( group x )
别忘了对列表进行排序,group
适用于连续的元素。
any ( . . . ) ( group ( sort x ) )
您可以将谓词(不是。null。tail)用作选项之一。
答案 1 :(得分:1)
昨天我发布了类似的算法here。可能的解决方法是
{}, {x0}, {x0,x1}, {x0,x1,x2} ...
x0, x1 , x2 , x3 ...
{}, {x0}, {x0,x1}, {x0,x1,x2} ...
xi
这样xi ∈ {x0..xi-1}
例如,可以通过以下功能实现。
首先,我们使用scanl
将列表中的元素迭代地添加到集合中,从而生成这些迭代的累积序列。
sets :: [Int] -> [Set Int]
sets = scanl (\s x -> insert x s) empty
然后我们按此顺序压缩原始列表,因此每个xi
与{x0...xi-1}
配对。
elsets :: [Int] -> [(Int, Set Int)]
elsets xs = zip xs (sets xs)
最后,我们使用find
在已经包含该元素的集合中搜索“即将插入”的元素。函数find返回对元素/集合,然后我们进行模式匹配以仅保留元素并返回。
result :: [Int] -> Maybe Int
result xs = do (x,_) <- find(\(y,s)->y `elem` s) (elsets xs)
return x
答案 2 :(得分:1)
以下使用Data.Map
的另一种方法效率不及..group . sort..
解决方案,它仍然是O(n log n)
但可以使用无限列表。
import Data.Map.Lazy as Map (empty, lookup, insert)
identical :: [Int] -> Bool
identical = loop Map.empty
where loop _ [] = False
loop m (x:xs) = if Map.lookup x m == Nothing
then loop (insert x 0 m) xs
else True
答案 3 :(得分:0)
好的,这基本上是您真正需要sort
来提高效率的罕见情况之一。实际上,Data.List.Unique
包仅具有此功能的repeated
功能,如果选中了源,则可以看到选择了sort
和group
策略。我猜这不是最有效的算法。我将讨论如何提高sort
的效率,但是暂时让我们享受一下,因为这是一个很好的问题。
因此,我们在tails :: [a] -> [[a]]
包中提供了Data.List
函数。因此;
*Main> tails [3,3,6,1]
[[3,3,6,1],[3,6,1],[6,1],[1],[]]
您很快就会注意到,我们可以zipWith
tail
的{{1}}列表中的tails
,并使用给定的原始列表通过应用函数来检查{ {1}}项不同。此函数可以是列表理解,也可以只是[[3,6,1],[6,1],[1],[]
函数。问题是,我想使all
短路,以便一旦遇到第一个被骗者,我们就停止all :: Foldable t => (a -> Bool) -> t a -> Bool
并检查其余部分,以免浪费工作。为此,我可以使用zipWith
的单字版本,即zipWith
,它位于zipWith
包中。原因是,根据类型签名,我们了解到,如果我的monad恰好是zipWithM :: Applicative m => (a -> b -> m c) -> [a] -> [b] -> m [c]
或{{,则在中间占Control.Monad
或Nothing
时,它将停止进一步计算1}}。
哦..!在Haskell中,我也喜欢使用Left whatever
函数而不是Maybe
和Either
。 bool :: a -> a -> Bool -> a
是Haskell的三元运算,类似
if
否定选择在左边,正选在右边,其中then
是在咖啡时间返回bool
的函数。也很可组合..太酷了..!
因此,由于我们现在已经掌握了所有背景知识,因此可以继续执行代码
bool "work time" "coffee break" isCoffeeTime
但是,正如我所说,这仍然是一种幼稚的方法,因为从理论上讲,我们正在执行isCoffeeTime :: Bool
操作。是的,True
如果第一个遇到的重复对象靠近头部,则可以大大减少冗余,但是该算法仍为O(n ^ 2)。
我认为在这种情况下,最好使用Haskell的天堂般的import Control.Monad (zipWithM)
import Data.List (tails)
import Data.Bool (bool)
anyDupe :: Eq a => [a] -> Either a [a]
anyDupe xs = zipWithM f xs ts
where ts = tail $ tails xs
f = \x t -> bool (Left x) (Right x) $ all (x /=) t
*Main> anyDupe [1,2,3,4,5]
Right [1,2,3,4,5] -- no dupes so we get the `Right` with the original list
*Main> anyDupe [3,3,6,1]
Left 3 -- here we have the first duplicate since zipWithM short circuits.
*Main> anyDupe $ 10^7:[1..10^7]
Left 10000000 -- wow zipWithM worked and returned reasonably fast.
算法(这并不是我们所知道的合并排序)。
现在,算法奖颁给了-> 此处的鼓声-> n(n+1)/2
和zipWithM
-> 掌声。对不起,没有分组。
所以现在...我们将再次使用单子技巧来利用短路。我们将使用sort
。与sort
monad一起使用时,这还使我们可以返回更有意义的结果。好的,让我们一起做。任何fold
表示foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b
是第一个重复项,没有更多的计算,而任何Either
则表示没有重复项。
Left n
在现实世界中,n
应该永远是赢家。