I have a list of 3 tuples items, I would like to index the list based on the first item, I have already written a code that sounds logically sane to me yet am getting a type error, here's what I wrote
addIndex [] indexed = indexed
addIndex ((a1,b1,c1):xs) []
= addIndex xs [(a1,b1,c1,0)]
addIndex ((a1,b1,c1):xs) indexedWIP
= addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix)
= if (a1 /= ax)
then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1))))
else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))
I'm getting the following type error
ERROR file:.\lmaogetrektson.hs:109 - Type error in application
*** Expression : indexedWIP ++ (a1,b1,c1,ix + 1)
*** Term : (a1,b1,c1,ix + 1)
*** Type : (b,c,d,e)
*** Does not match : [a]
Let me examine the types of your addIndex at each row:
addIndex :: [a] -> b -> b
addIndex [] indexed = indexed
-- Combined with the above, leads to:
addIndex :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> [(a,b,c,n)]
addIndex ((a1,b1,c1):xs) [] = addIndex xs [(a1,b1,c1,0)]
-- This call demands addIndexH satisfies:
addIndexH :: (Num n) => [(a,b,c)] -> [(a,b,c,n)] -> (a,b,c,n) -> [(a,b,c,n)]
-- It's also costly, as last needs to traverse the list
addIndex ((a1,b1,c1):xs) indexedWIP =
addIndexH ((a1,b1,c1):xs) indexedWIP (last indexedWIP)
-- /= check matches types of a1 and ax, requiring them to be Eq
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) =
if (a1 /= ax) then (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix+1))))
else (addIndex xs (indexedWIP ++ (a1,b1,c1,(ix))))
The distinction of list and tuple is actually the problem you hit here.
Prelude> :t (++)
(++) :: [a] -> [a] -> [a]
Both operands to ++ must be same-type lists. So we need something like:
addIndexH ((a1,b1,c1):xs) indexedWIP (ax,bx,cx,ix) =
if (a1 /= ax) then (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix+1))]))
else (addIndex xs (indexedWIP ++ [(a1,b1,c1,(ix))]))
The end result should be a function that takes a list of 3-tuples and another list of enumerated 4-tuples, but in a rather circuitous manner. Consider how it expands:
addIndex [(a,b,c), (x,y,z)] []
addIndex [(x,y,z)] [(a,b,c,0)]
addIndexH [(x,y,z)] [(a,b,c,0)] (a,b,c,0)
addIndex [] ([(a,b,c,0)] ++ [(x,y,z,(0+1))])
([(a,b,c,0)] ++ [(x,y,z,(0+1))])
That's a fairly complex procedure, and it grows worse the longer the lists are (we haven't even looked at duplicate a fields yet).
When you do encounter a duplicate a field, you still append it, only keeping the new index value. This means, since we only checked against the last item, that we have two items of matching a and index right next to each other. The function could be rewritten in several ways, in particular without rebuilding lists of every intermediate length and traversing the growing one for each element.
I think you make it more complex than necessary. If I understand it correctly, you take as input a list of 3-tuples (a, b, c), and you want to return a list of 4-tuples (a, b, c, i), where i specifies the thus far number of different a-values we observed.
We thus perform some sort of mapping but with an accumulator. Although we can use higher-order constructs here, let us here aim to use recursion and add an accumulator. We can first define a helper function with signature:
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
where the first parameter is thus the a-value of the previous element (we here assume that we processed already an element). The second parameter is the number of elements we thus far observed, the third elements is the list of elements we still have to process, and the result is the list of 4-tuples.
In case the list is exhausted, then we can return the empty list, regardless of the other variables:
addIndex' _ _ [] = []
in the other case, we should compare the previous key ap with the current key a, and in case the two are equal, we return the tuple with the index i as last element, we then recurse with the same index; otherwise we increment the index (to i1 = i + 1). We each time recurse on the tail of the list:
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
So we obtain the function:
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
But now we still have to process the first element. We know that if the list is empty, we return the empty list:
addIndex [] = []
otherwise we return as first tuple the first one in the given list with index 0, and then make a call to addIndex' with the remaining tuples and the first key as accumulator:
addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs
so we obtain as full solution:
addIndex :: (Num n, Eq a) => [(a, b, c)] -> [(a, b, c, n)]
addIndex [] = []
addIndex ((a, b, c): xs) = (a, b, c, 0) : addIndex' a 0 xs
addIndex' :: (Num n, Eq a) => a -> n -> [(a, b, c)] -> [(a, b, c, n)]
addIndex' _ _ [] = []
addIndex' ap i ((a, b, c): xs) | a == ap = (a, b, c, i) : addIndex' a i xs
| otherwise = (a, b, c, i1) : addIndex' a i1 xs
where i1 = i + 1
Then we generate for example:
Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2)]
But note that we only look at the previous element, and hence for example if the 'a' key occurs after 'c', we will increment the counter again:
Prelude> addIndex [('a', 1, 4), ('a', 2, 5), ('b', 1, 3), ('b', 0, 2), ('c', 1, 2), ('a', 3, 4)]
[('a',1,4,0),('a',2,5,0),('b',1,3,1),('b',0,2,1),('c',1,2,2),('a',3,4,3)]
This function will run in linear time O(n) whereas the functions you composed will run in quadratic time O(n2) since appending is done in linear time (as well as last, etc.).
Related
This is an anti-symmetrical recursive function that will take a list of pairs If the list is anti-symmetrical then an empty list will return and if the list is symmetrical it will return only the two pairs that are symmetrical.
Here is an example output.
val antisymmetric_rel = [(1,2), (2,3), (4,4), (5,9), (10,9), (0,0)];
antisymmetricCounterEx(antisymmetric_rel);
(* [] *)
val not_antisymmetric_rel = [(1,2), (2,3), (4,4), (5,9), (10,9), (9,5)];
(* [((5,9),(9,5))] *)
Here is what I have so far.
fun antisymmetricCounterEx([]) = []
| antisymmetricCounterEx(relation) =
let
fun testOne((a, b), []) = []
| testOne((a, b), (c, d)::rest) =
(if (not (a = d)) orelse testOne((b, d), rest) ) then []
else [(a, b)] # testOne((c, d), rest);
fun testAll([]) = []
| testAll((a, b)::rest) =
testOne((a, b), relation) # testAll(rest);
in
testAll(relation)
end;
I am getting errors that I cannot understand but it seems that the type and operands are ok.
First off, let's clean up your code a bit.
We can remove the parentheses here: (if (not (a = d)) orelse testOne((b, d), rest) ) then [] which break the syntax, as well as generally eliminate extraneous parens throughout the code.
not (a = d) can be written as a <> d
[(a, b)] # testOne((c, d), rest) is replaced with (a, b) :: testOne((c, d), rest)
fun antisymmetricCounterEx([]) = []
| antisymmetricCounterEx(relation) =
let
fun testOne((a, b), []) = []
| testOne((a, b), (c, d)::rest) =
if a <> d orelse testOne((b, d), rest) then []
else (a, b) :: testOne((c, d), rest);
fun testAll([]) = []
| testAll((a, b)::rest) =
testOne((a, b), relation) # testAll(rest);
in
testAll(relation)
end;
This still fails because as pointed out in comments testOne returns a list:
fun testOne((a, b), []) = []
But it's used in a boolean context.
Really what you have is a filtering exercise. You need to filter for any pair that has a symmetrical counterpart in the list. However, you need to account for pairs that are symmetrical with themselves, like (4, 4) and (0, 0).
To aid with this, let's write a list_count function that can count the number of elements in a list that fulfill a predicate function.
fun list_count _ [] = 0
| list_count f (x::xs) =
(if f x then 1 else 0) + list_count f xs;
We can now use List.filter and list_count to achieve the desired result.
fun sym_pairs(pairs) =
List.filter
(fn (a, b) =>
let
val c = list_count (fn (c, d) => a = d andalso b = c) pairs
in
if a = b then c > 1
else c = 1
end)
pairs;
Now:
sym_pairs([(1,2), (2,3), (4,4), (5,9), (10,9), (0,0)]);
Yields: []. And:
sym_pairs([(1,2), (2,3), (4,4), (5,9), (10,9), (9,5)]);
Yields: [(5, 9), (9,5)].
I am new to Haskell and I want to extract the maximum element from a given List so that I end up with the maximum element x and the remaining list xs (not containing x). It can be assumed that the elements of the list are unique.
The type of function I want to implement is somewhat like this:
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
Notably, the first argument is a function that turns an element into a comparable form. Also, this function is non-total as it would fail given an empty List.
My current approach fails to keep the elements in the remainder list in place, meaning given [5, 2, 4, 6] it returns (6, [2, 4, 5]) instead of (6, [5, 2, 4]). Furthermore, it feels like there should be a nicer looking solution.
compareElement :: (Ord b) => (a -> b) -> a -> (b, (a, [a])) -> (b, (a, [a]))
compareElement p x (s, (t, ts))
| s' > s = (s', (x, t:ts))
| otherwise = (s, (t, x:ts))
where s' = p x
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement p (t:ts) = snd . foldr (compareElement p) (p t, (t, [])) $ ts
UPDATE
Thanks to the help of the answer of #Ismor and the comment #chi I've updated my implementation and I feel happy with the result.
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a], [a])
maxElement p =
let
f x Nothing = Just (p x, x, [], [x])
f x (Just (s, m, xs, ys))
| s' > s = Just (s', x, ys, x:ys)
| otherwise = Just (s, m, x:xs, x:ys)
where s' = p x
in
foldr f Nothing
The result is either Nothing when the given list is empty or Maybe (_, x, xs, _). I could write another "wrapper" function with the originally intended type and call maxElement under the hood, but I believe this also ok.
This answer is more of a personal advise than a proper answer. As a rule of thumb, whenever you find yourself trying to write a loop with an accumulator (as in this case), try to write it in this form
foldr updateAccumulator initialAccumulator --use foldl' if it is better for your use case`
then, follow the types to complete It as shown below
Step 1
Write undefined where needed. You know the function should look like this
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
updateAccumulator = undefined
initialAccumulator = undefined
Step 2
"Chase the type". Meaning that using the type of maxElement and foldr you can
deduce the types of updateAccumulator and initialAccumulator. Try to reduce polymorphism as much as you can. In this case:
You know foldr :: Foldable t => (a -> b -> b) -> b -> t a -> b
You know your Foldable is [] so It'd be easier to substitute
Hence foldr :: (a -> b -> b) -> b -> [a] -> b
Because you want foldr to produce (a, [a]) you know b ~ (a, [a])
etc... keep going until you know what types your functions have. You can use ghc typed holes in this process, which is a very nice feature
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
-- Notice that you need to enable an extension to write type signature in where clause
-- updateAccumulator :: a -> (a, [a]) -> (a, [a])
updateAccumulator newElement (currentMax, currentList) = undefined
-- initialAccumulator :: (a, [a])
initialAccumulator = undefined
Step 3
Now, writing down the function should be easier. Below I leave some incomplete parts for you to fill
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = foldr updateAccumulator initalAccumulator xs
where
-- updateAccumulator :: a -> (a, [a]) -> (a, [a])
updateAccumulator newElement (currentMax, currentList) =
if f newElement > f currentMax
then undefined -- How does the accumulator should look when the new element is bigger than the previous maximum?
else undefined
-- initialAccumulator :: (a, [a])
initialAccumulator = undefined -- Tricky!, what does happen if xs is empty?
Hope this clarifies some doubts, and understand I don't give you a complete answer.
I don't know if you were trying to avoid using certain library functions, but Data.List has a maximumBy and deleteBy that do exactly what you want:
import Data.Function (on)
import Data.List (deleteBy, maximumBy)
import Data.Ord (comparing)
maxElement :: (Ord b) => (a -> b) -> [a] -> (a, [a])
maxElement f xs = (max, remaining) where
max = maximumBy (comparing f) xs
remaining = deleteBy ((==) `on` f) max xs
Thanks to the help of the answer of #Ismor and the comment #chi I've updated my implementation and I feel happy with the result.
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a], [a])
maxElement p =
let
f x Nothing = Just (p x, x, [], [x])
f x (Just (s, m, xs, ys))
| s' > s = Just (s', x, ys, x:ys)
| otherwise = Just (s, m, x:xs, x:ys)
where s' = p x
in
foldr f Nothing
The result is either Nothing when the given list is empty or Maybe (_, x, xs, _). I could write another "wrapper" function with the originally intended type and call maxElement under the hood, but I believe this is also ok.
Construct the list of all the "zippers" over the input list, then take the maximumBy (comparing (\(_,x,_) -> foo x)) of it, where foo is your Ord b => a -> b function, then reverse-append the first half to the second and put it in a tuple together with the middle element.
A zipper over a list xs is a triple (revpx, x, suffx) where xs == reverse revpx ++ [x] ++ suffx:
> :t comparing (\(_,x,_) -> x)
comparing (\(_,x,_) -> x)
:: Ord a => (t, a, t1) -> (t, a, t1) -> Ordering
Constructing the zippers list is an elementary exercise (see the function picks3 there).
About your edited solution, it can be coded as a foldr over the tails so it's a bit clearer what's going on there:
maxElement :: (Ord b) => (a -> b) -> [a] -> Maybe (b, a, [a])
maxElement p [] = Nothing
maxElement p xs = Just $ foldr f undefined (tails xs)
where
f [x] _ = (p x, x, [])
f (x:xs) (b, m, ys)
| b' > b = (b', x, xs) -- switch over
| otherwise = (b, m, x:ys)
where b' = p x
It's also a bit cleaner as it doesn't return the input list's copy for no apparent reason, as your version did since it used it for internal purposes.
Both ways are in fact emulating a paramorphism.
How I can iterate a list of elements four by four and then add these four card to a tuple?.
Basically this a function that receives the Suit that it was to be killed, the cards played by order and the player who has played first.
That game is played by pairs, so it returns a tuple with the cards won for the first couple and the cards won for the second couple.
winnedCards:: Suit-> [Card] -> Int -> ([Card],[Card])
winnedCards [] _ = ([],[])
winnedCards (Suit type) cardsPlayed positionFirstPlayPerson
| snd(xs) == 3 = (take 4 (cardsPlayed),[])
| snd(xs) == 1 = (take 4 (cardsPlayed),[])
| otherwise = ([],take 2 (cardsPlayed))
where xs = whoHasWon (take 4 (cardsPlayed)) (Suit type)
whoHasWon returns the position of the player who has won. I think I have to it recursevily because I have to iterate 4 by 4, and then add the result to the tuple after each iteration.
As Willem Van Onsem mentions in the comments, your function appears to do entirely too much work. If you're trying to split a list into groups of four-tuples, do that separately from the rest of the logic.
toFourTuple :: [a] -> [(a, a, a, a)]
toFourTuple [] = []
toFourTuple (a:b:c:d:rest) = (a, b, c, d) : toFourTuple rest
toFourTuple _ = error "list not divisible by four"
-- how should this be handled?
For the rest, it might be useful to know that the Monoid instance of Monoid a => (a, a) implements mappend as mappend (x, y) (x', y') = (x <> x', y <> y'), so if you generate a list of (leftside, rightside) sublists, you can mconcat them together.
xs = [ ([1] , [3])
, ([2, 4], [5])
, ([6] , [7, 9, 11, 13])
]
mconcat xs = ([1,2,4,6],[3,5,7,9,11,13])
Of course this works incrementally too.
-- |Separates [Int] into ([odds], [evens])
splitByMod2 :: [Int] -> ([Int], [Int])
splitByMod2 [] = ([], [])
splitByMod2 (x:xs)
| odd x = ([x], []) `mappend` splitByMod2 xs
| even x = ([], [x]) `mappend` splitByMod2 xs
| otherwise = error "This cannot happen"
I am trying to learn Haskell and I want to solve one task. I have a list of Integers and I need to add them to another list if they are bigger then both of their neighbors. For Example:
I have a starting list of [0,1,5,2,3,7,8,4] and I need to print out a list which is [5, 8]
This is the code I came up but it returns an empty list:
largest :: [Integer]->[Integer]
largest n
| head n > head (tail n) = head n : largest (tail n)
| otherwise = largest (tail n)
I would solve this as outlined by Thomas M. DuBuisson. Since we want the ends of the list to "count", we'll add negative infinities to each end before creating triples. The monoid-extras package provides a suitable type for this.
import Data.Monoid.Inf
pad :: [a] -> [NegInf a]
pad xs = [negInfty] ++ map negFinite xs ++ [negInfty]
triples :: [a] -> [(a, a, a)]
triples (x:rest#(y:z:_)) = (x,y,z) : triples rest
triples _ = []
isBig :: Ord a => (a,a,a) -> Bool
isBig (x,y,z) = y > x && y > z
scnd :: (a, b, c) -> b
scnd (a, b, c) = b
finites :: [Inf p a] -> [a]
finites xs = [x | Finite x <- xs]
largest :: Ord a => [a] -> [a]
largest = id
. finites
. map scnd
. filter isBig
. triples
. pad
It seems to be working appropriately; in ghci:
> largest [0,1,5,2,3,7,8,4]
[5,8]
> largest [10,1,10]
[10,10]
> largest [3]
[3]
> largest []
[]
You might also consider merging finites, map scnd, and filter isBig in a single list comprehension (then eliminating the definitions of finites, scnd, and isBig):
largest :: Ord a => [a] -> [a]
largest xs = [x | (a, b#(Finite x), c) <- triples (pad xs), a < b, c < b]
But I like the decomposed version better; the finites, scnd, and isBig functions may turn out to be useful elsewhere in your development, especially if you plan to build a few variants of this for different needs.
One thing you might try is lookahead. (Thomas M. DuBuisson suggested a different one that will also work if you handle the final one or two elements correctly.) Since it sounds like this is a problem you want to solve on your own as a learning exercise, I’ll write a skeleton that you can take as a starting-point if you want:
largest :: [Integer] -> [Integer]
largest [] = _
largest [x] = _ -- What should this return?
largest [x1,x2] | x1 > x2 = _
| x1 < x2 = _
| otherwise = _
largest [x1,x2,x3] | x2 > x1 && x2 > x3 = _
| x3 > x2 = _
| otherwise = _
largest (x1:x2:x3:xs) | x2 > x1 && x2 > x3 = _
| otherwise = _
We need the special case of [x1,x2,x3] in addition to (x1:x2:x3:[]) because, according to the clarification in your comment, largest [3,3,2] should return []. but largest [3,2] should return [3]. Therefore, the final three elements require special handling and cannot simply recurse on the final two.
If you also want the result to include the head of the list if it is greater than the second element, you’d make this a helper function and your largest would be something like largest (x1:x2:xs) = (if x1>x2 then [x1] else []) ++ largest' (x1:x2:xs). That is, you want some special handling for the first elements of the original list, which you don’t want to apply to all the sublists when you recurse.
As suggested in the comments, one approach would be to first group the list into tuples of length 3 using Preludes zip3 and tail:
*Main> let xs = [0,1,5,2,3,7,8,4]
*Main> zip3 xs (tail xs) (tail (tail xs))
[(0,1,5),(1,5,2),(5,2,3),(2,3,7),(3,7,8),(7,8,4)]
Which is of type: [a] -> [b] -> [c] -> [(a, b, c)] and [a] -> [a] respectively.
Next you need to find a way to filter out the tuples where the middle element is bigger than the first and last element. One way would be to use Preludes filter function:
*Main> let xs = [(0,1,5),(1,5,2),(5,2,3),(2,3,7),(3,7,8),(7,8,4)]
*Main> filter (\(a, b, c) -> b > a && b > c) xs
[(1,5,2),(7,8,4)]
Which is of type: (a -> Bool) -> [a] -> [a]. This filters out elements of a list based on a Boolean returned from the predicate passed.
Now for the final part, you need to extract the middle element from the filtered tuples above. You can do this easily with Preludes map function:
*Main> let xs = [(1,5,2),(7,8,4)]
*Main> map (\(_, x, _) -> x) xs
[5,8]
Which is of type: (a -> b) -> [a] -> [b]. This function maps elements from a list of type a to b.
The above code stitched together would look like this:
largest :: (Ord a) => [a] -> [a]
largest xs = map (\(_, x, _) -> x) $ filter (\(a, b, c) -> b > a && b > c) $ zip3 xs (tail xs) (tail (tail xs))
Note here I used typeclass Ord, since the above code needs to compare with > and <. It's fine to keep it as Integer here though.
So basically I have a list of tuples [(a,b)], from which i have to do some filtering. One job is to remove inverted duplicates such that if (a,b) and (b,a) exist in the list, I only take one instance of them. But the list comprehension has not been very helpful. How to go about this in an efficient manner?
Thanks
Perhaps an efficient way to do so (O(n log(n))) would be to track the tuples (and their reverses) already added, using Set:
import qualified Data.Set as Set
removeDups' :: Ord a => [(a, a)] -> Set.Set (a, a) -> [(a, a)]
removeDups' [] _ = []
removeDups' ((a, b):tl) s | (a, b) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s | (b, a) `Set.member` s = removeDups' tl s
removeDups' ((a, b):tl) s = ((a, b):rest) where
s' = Set.insert (a, b) s
rest = removeDups' tl s'
removeDups :: Ord a => [(a, a)] -> [(a, a)]
removeDups l = removeDups' l (Set.fromList [])
The function removeDups calls the auxiliary function removeDups' with the list, and an empty set. For each pair, if it or its inverse are in the set, it is passed; otherwise, both it and its inverses are added, and the tail is processed. \
The complexity is O(n log(n)), as the size of the set is at most linear in n, at each step.
Example
...
main = do
putStrLn $ show $ removeDups [(1, 2), (1, 3), (2, 1)]
and
$ ghc ord.hs && ./ord
[1 of 1] Compiling Main ( ord.hs, ord.o )
Linking ord ...
[(1,2),(1,3)]
You can filter them using your own function:
checkEqTuple :: (a, b) -> (a, b) -> Bool
checkEqTuple (x, y) (x', y') | (x==y' && y == x') = True
| (x==x' && y == y') = True
| otherwise = False
then use nubBy
Prelude Data.List> nubBy checkEqTuple [(1,2), (2,1)]
[(1,2)]
I feel like I'm repeating myself a bit, but that's okay. None of this code had been tested or even compiled, so there may be bugs. Suppose we can impose an Ord constraint for efficiency. I'll start with a limited implementation of sets of pairs.
import qualified Data.Set as S
import qualified Data.Map.Strict as M
newtype PairSet a b =
PS (M.Map a (S.Set b))
empty :: PairSet a b
empty = PS M.empty
insert :: (Ord a, Ord b)
=> (a, b) -> PairSet a b -> PairSet a b
insert (a, b) (PS m) = PS $ M.insertWith S.union a (S.singleton b) m
member :: (Ord a, Ord b)
=> (a, b) -> PairSet a b -> Bool
member (a, b) (PS m) =
case M.lookup a m of
Nothing -> False
Just s -> S.member b s
Now we just need to keep track of which pairs we've seen.
order :: Ord a => (a, a) -> (a, a)
order p#(a, b)
| a <= b = p
| otherwise = (b, a)
nubSwaps :: Ord a => [(a,a)] -> [(a,a)]
nubSwaps xs = foldr go (`seq` []) xs empty where
go p r s
| member op s = r s
| otherwise = p : r (insert op s)
where op = order p
If a and b are ordered and compareable, you could just do this:
[(a,b) | (a,b) <- yourList, a<=b]