Haskell - 检查所有列表元素是否唯一

时间:2015-06-24 20:32:51

标签: list haskell functional-programming comparison

我需要比较给定列表的所有元素是否唯一。 (为了记录,我这样做是出于学术目的。)

这是我到目前为止所做的:

allDifferent :: (Eq a) => [a] -> Bool
allDifferent list = case list of
    []      -> True
    (x:xs)  -> if x `elem` xs then False else allDifferent xs

哪作得非常好!

现在,当我尝试这样做时......

allDifferent2 :: (Eq a) => [a] -> Bool
allDifferent2 list
    | null list                                                     = True        
    | (head list) `elem` (tail list) || allDifferent2 (tail list)  = False
    | otherwise  

它不能按预期工作。 我从GHCi得到以下输出:

*Main> allDifferent2 [1..4]
False
*Main> allDifferent2 [1..5]
True
*Main> allDifferent2 [1..6]
False
*Main> allDifferent2 [1..7]
True

即。对于具有偶数元素的每个列表,它输出False,对于奇数量的元素,输出True。

我错过了什么? 有人会照顾一些光吗?

7 个答案:

答案 0 :(得分:7)

另一种利用notElem

的方法
allDifferent :: (Eq a) => [a] -> Bool
allDifferent list = case list of
    []      -> True
    (x:xs)  -> x `notElem` xs && allDifferent xs

次要变体,直接在方程中使用模式匹配:

allDifferent :: (Eq a) => [a] -> Bool
allDifferent []     = True
allDifferent (x:xs) = x `notElem` xs && allDifferent xs

我倾向于远离像head,tail这样的部分功能,所以基于守卫的变种看起来更糟糕。

答案 1 :(得分:4)

我会这样做。递归+ elem O(n²)。或者,您可以先对列表进行排序,然后成对比较元素。这样排序是 O(n⋅logn),遍历 O(n)。所以整体 O(n⋅logn)

import Data.List

allDifferent :: (Ord a, Eq a) => [a] -> Bool
allDifferent = comparePairwise.sort

comparePairwise :: Eq a => [a] -> Bool
comparePairwise [] = True
comparePairwise [_] = True
comparePairwise (x:y:xs) 
    | x == y = False
    | otherwise = comparePairwise (y : xs)

答案 2 :(得分:1)

试试这个:

allDifferent2::(Eq a) => [a] -> Bool
allDifferent2 list
    | list == []                        = True
    | (head list) `elem` (tail list)    = False
    | otherwise = allDifferent2(tail list)

如果列表为[],则应返回True(如@bheklilr所说:))

如果列表不为null,则可以验证第一个元素是否在列表的尾部。如果是,则返回False。好。

但是当你说“如果它在列表的尾部 OR allDifferent2(尾部列表)”时,你就是在杀死你的函数。 “如果此列表中的所有元素都不同,请返回 FALSE ”,这不是您想要的。

编辑:是的,它会@Luis。我通过把“否则”放在那里来解决这个问题。当我在allDifferent2(尾部列表)之前放置警卫时,它检查此函数是否返回True。因此它适用于[1,1,2](我的测试用例)但不适用于[1,2,2](与你的情况类似)。

答案 3 :(得分:1)

我能想到的最简单合理的惯用法是

import Data.Maybe

pairwiseDifferent xs = foldr go (const True) xs Nothing
  where
    go x k Nothing = k (Just x)
    go x k (Just prev) = x /= prev && k (Just x)

有关折叠的乐趣,

Set

另一种选择是使用import qualified Data.Set as S allDifferent xs = foldr go (\s -> s `seq` True) xs S.empty where go x k s | S.member x s = False | otherwise = k $! S.insert x s (某些严格注释可能实际上不是必需的):

<html>
<head>
<title>Enormous Wedding III</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>

<div class = "container"><span>
<div id = "game"><iframe src='...'></div>
<div id = "video"><video autoplay> <source src="menu.mp4" type="video/mp4">
</video></div>
</span></div>

<script>
    var video = document.getElementsByTagName('video')[0];
    var game = document.getElementsByTagName('iframe')[0];

    video.onended = function(e) {
      video.style.display = "none";
    };
</script>

</body></html>

答案 4 :(得分:1)

您可以依赖库函数:allDifferent xs = nub xs == xs

或者,用无点符号写成:allDifferent = uncurry (==) . (nub &&& id)

使用Data.Discrimination.nub,这发生在 O(n)时间。

答案 5 :(得分:1)

将列表,group次相同元素的运行排序在一起,并检查all组是否只有一个元素。

import Data.List (group, sort)

pairwiseDistinct :: Ord a => [a] -> Bool
pairwiseDistinct xs = all (\ys -> null (tail ys)) (group (sort xs))

无点版本:

pairwiseDistinct = all (null . tail) . group . sort

这假设任何两个元素xyx == y当且仅当compare x y == EQ

tail在这里没问题,因为没有一个组会变空,但如果你反对部分函数,​​你可以替换drop 1

答案 6 :(得分:0)

allDifferent []    = True
allDifferent (h:t) = 
                     let (e,(l,r)) = segment h t 
                     in e && allDifferent l && allDifferent r

segment p []     = (True,([],[])))
segment p (h:s) 
   | p > h       = let (e,(l,r)) = segment p s in (e,(l,h:r))
   | p < h       = let (e,(l,r)) = segment p s in (e,(h:l,r))
   | otherwise   = (False,([],[])))

如您所见,此解决方案的结构与quickSort非常相似。 它共享一个二叉树作为中间数据结构,因此时间复杂度非常相似。