检查列表列表是否具有两个或更多相同的元素

时间:2018-12-15 11:09:53

标签: list haskell filter

我需要编写一个函数,该函数检查列表中是否包含两个或更多相同的元素并返回true或false。

例如[3,3,6,1]应该返回true,但是[3,8]应该返回false。

这是我的代码:

identical :: [Int] -> Bool
identical x = (\n-> filter (>= 2) n )( group x )

我知道这很糟糕,并且不起作用。 我想将列表分为列表列表,如果列表的长度> = 2,则返回true,否则返回false。

4 个答案:

答案 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功能,如果选中了源,则可以看到选择了sortgroup策略。我猜这不是最有效的算法。我将讨论如何提高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.MonadNothing时,它将停止进一步计算1}}。

哦..!在Haskell中,我也喜欢使用Left whatever函数而不是MaybeEitherbool :: 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)/2zipWithM-> 掌声。对不起,没有分组。

所以现在...我们将再次使用单子技巧来利用短路。我们将使用sort。与sort monad一起使用时,这还使我们可以返回更有意义的结果。好的,让我们一起做。任何fold表示foldM :: (Foldable t, Monad m) => (b -> a -> m b) -> b -> t a -> m b是第一个重复项,没有更多的计算,而任何Either则表示没有重复项。

Left n

在现实世界中,n应该永远是赢家。