Reputation: 393
I need to compare if all elements of a given list are unique. (For the record I am doing so for academic purposes.)
Here is what I have thus far:
allDifferent :: (Eq a) => [a] -> Bool
allDifferent list = case list of
[] -> True
(x:xs) -> if x `elem` xs then False else allDifferent xs
Which works wonderfully!
Now, when I try to do it like this...
allDifferent2 :: (Eq a) => [a] -> Bool
allDifferent2 list
| null list = True
| (head list) `elem` (tail list) || allDifferent2 (tail list) = False
| otherwise
It just doesn't work as intended. I get the following output from GHCi:
*Main> allDifferent2 [1..4]
False
*Main> allDifferent2 [1..5]
True
*Main> allDifferent2 [1..6]
False
*Main> allDifferent2 [1..7]
True
i.e. For every list with an even amount of elements it outputs False and for an odd amount of elements, True.
What am I missing? Would anyone care to shine some light?
Upvotes: 5
Views: 10633
Reputation: 9
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,([],[])))
As you can see the structure of this solution is very similar to quickSort. It shares as an intermediate data structure a binary tree and for that reason, the time complexity is extremely similar.
Upvotes: 0
Reputation: 7579
Sort the list, group
runs of equal elements together, and check if all
groups have exactly one element.
import Data.List (group, sort)
pairwiseDistinct :: Ord a => [a] -> Bool
pairwiseDistinct xs = all (\ys -> null (tail ys)) (group (sort xs))
Point-free version:
pairwiseDistinct = all (null . tail) . group . sort
This assumes that for any two elements x
and y
, x == y
if and only if compare x y == EQ
.
tail
is fine here because none of the groups will ever be empty, but you can substitute drop 1
if you're averse to partial functions.
Upvotes: 1
Reputation: 16105
You can rely on library functions: allDifferent xs = nub xs == xs
.
Or, written in point-free notation: allDifferent = uncurry (==) . (nub &&& id)
.
Using Data.Discrimination.nub, this happens in O(n) time.
Upvotes: 3
Reputation: 48580
The simplest reasonable idiomatic approach I can think of is
allDifferent :: Ord a => [a] -> Bool
allDifferent = pairwiseDifferent . sort
pairwiseDifferent :: Eq a => [a] -> Bool
pairwiseDifferent xs = and $ zipWith (/=) xs (drop 1 xs)
For fun with folds,
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)
Another option is to use a Set
(some of the strictness annotations may not actually be necessary):
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
Upvotes: 2
Reputation: 18299
I would do this differently. Recursion + elem
is O(n²). Alternatively you can first sort the list, and then compare elements pairwise. This way the sorting is O(n⋅log n), and the traversal O(n). So overall O(n⋅log n):
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)
Upvotes: 5
Reputation: 619
Try this:
allDifferent2::(Eq a) => [a] -> Bool
allDifferent2 list
| list == [] = True
| (head list) `elem` (tail list) = False
| otherwise = allDifferent2(tail list)
If the list is [] you should return True (As @bheklilr said :) )
If the list isn't null, you can verify if the first element is in the tail of the list. If it is, return False. Okay.
But when you say "if it is in the tail of the list OR allDifferent2 (tail list)" you are killing your function. "If all the elements are different in this list, return FALSE", and that isn't what you want.
EDIT: Yeah, it will @Luis. I fixed that by putting that "otherwise" there. When I put the guard before the allDifferent2(tail list) it checked if this function returned True. Thus it would work for [1, 1, 2] (my test-case) but not for [1, 2, 2] (similar to your case).
Upvotes: 1
Reputation: 116139
An alternative exploiting notElem
:
allDifferent :: (Eq a) => [a] -> Bool
allDifferent list = case list of
[] -> True
(x:xs) -> x `notElem` xs && allDifferent xs
Minor variant, using pattern matching directly in the equations:
allDifferent :: (Eq a) => [a] -> Bool
allDifferent [] = True
allDifferent (x:xs) = x `notElem` xs && allDifferent xs
I tend to stay away from partial functions like head,tail
, so the variants based on guards look worse to me.
Upvotes: 9