2

Second Update:

I finally followed zurgl's suggestion and wrote something that traverses and groups recursively. Thanks to everyone who was trying to help!

First Update:

What I hoped could be a sorting function, but was unsure about, was meant to optimize a grouping that would follow (minimizing the number of groups). The grouping collects tuples that are adjacent horizontally or vertically:

f xs = 
  foldr (\a@(y,x) ((b@(y',x'):xs):bs) -> if (y == y' && abs (x-x') == 1) || 
                                            (x == x' && abs (y-y') == 1)
                                            then (a:b:xs):bs 
                                            else [a]:(b:xs):bs) 
                                            [[last xs]] (init xs)

Output of the second example, after the "sorting":

*Main> f [(0,1),(1,1),(2,1),(2,2),(2,3),(1,3),(0,3),(4,1),(4,2)]
[[(0,1),(1,1),(2,1),(2,2),(2,3),(1,3),(0,3)],[(4,1),(4,2)]]

--end of updates--

I'm having trouble conceiving of a sorting function and am hoping someone might have an idea about how to implement it or perhaps say if more might be needed than a custom sort. I've tried toying with sortBy but do not seem to be making much progress.

How do I get from this:

[(0,1),(0,3),(1,1),(1,3),(2,0),(2,1),(2,3),(4,1),(4,2)]

to this:

[(0,1),(1,1),(2,0),(2,1),(0,3),(1,3),(2,3),(4,1),(4,2)]

Differences of 0 or 1 between both y y' and x x' should be primary. Does that make sense?

Second example:

[(0,1),(0,3),(1,1),(1,3),(2,1),(2,2),(2,3),(4,1),(4,2)] 
=>
[(0,1),(1,1),(2,1),(2,2),(2,3),(1,3),(0,3),(4,1),(4,2)]
Community
  • 1
  • 1
גלעד ברקן
  • 23,602
  • 3
  • 25
  • 61
  • 2
    I don't understand what you mean by "Differences of 0 or 1 between both y y' and x x' should be primary." – yiding May 02 '13 at 03:29
  • Yeah, I don't understand it either. Can you explain why should e.g. (2,1) be before (0,3)? – mattiast May 02 '13 at 03:32
  • @yiding (2,1) goes before (0,3) because (2,1) has both y and x at a difference of <2 from the preceding element, which is (2,0). Thank you for asking. – גלעד ברקן May 02 '13 at 03:35
  • @mattiast please see my comment above to yiding. Thank you for asking. – גלעד ברקן May 02 '13 at 03:36
  • 2
    Not sure what you described can be expressed as an ordering, because you are not simply comparing `(2,1)` and `(0,3)` on their own, but in the context of another value `(2,0)`. For instance, given `[(0,0),(0,1),(0,2)]`, both that and `[(0, 2), (0, 1), (0, 0)]` would be valid. Please elaborate if I misunderstood your description. – yiding May 02 '13 at 03:41
  • @yiding your examples are correct, both are valid. Perhaps I'm looking for a mix of ordering with something more. – גלעד ברקן May 02 '13 at 03:48
  • 3
    I've voted to close: groovy's two (or three, look at Charles D's answer) examples do not explain what he wants, and trying to guess at possible orderings of pairs is a waste of everyone's time. – Charles Stewart May 02 '13 at 12:47
  • 3
    Your intentions are very unclear. I can recommend a strategy though. First, define a transformation function from a pair to a result type representing the ordering, e.g. a numeric result of an arithmetic operation. Second, use this function with [`sortWith`](http://hackage.haskell.org/packages/archive/base/latest/doc/html/GHC-Exts.html#v:sortWith) to sort your data. – Nikita Volkov May 02 '13 at 13:08
  • I don't know the goal of your work, but one stuff I was thinking about is that your stuff looks like a set of point in a space of dimension 2. And the reason your first try doesn't define an order as I demonstrate is link to the fact a referent point is missing. Then if fisrt we calculate the center of the set of point and then we have the reference and then we will be able to define an complete ordering upon your set of point using your function (which look like a sort a norm) and "the center point". Does my assumption make sense for you ? – zurgl May 02 '13 at 17:12
  • @zurgl Thank you for your effort -- to understand more, please read my update and the comments on the answer by Jean-Bernard Pellerin. (It was an idea to soleve another SO question, although I'm not sure this kind of sort is possible) – גלעד ברקן May 02 '13 at 17:16
  • You do not need to sort you listOfEdge in a special way. Doing like so `sort listOfEdge` is enough to produce an algorithm solving the initial problem. You take the first edge traverse the entire list until you find an adjacent edge, if it's ok you add this edge into the group else you close the group and start a next one poping a new edge from the listOfedge. Then you redo this process untill the listOfEdge is empty. A natural order (as defining by default in haskell) and the algorithm describe upper are enough to solve the problem, I guess. – zurgl May 02 '13 at 18:06
  • @zurgl yes, that might end up being a better solution. – גלעד ברקן May 02 '13 at 18:22
  • Following on zurgl's answer--what you are trying to do is not quite a sort (removing one element could cause the desired order of others to change). I get the impression that you just want to define a `takeClosest :: (Int, Int) -> [(Int, Int)] -> ((Int, Int), [(Int, Int)])` function and build up the list that way. It is not, however, going to be stable with respect to the original order of the input list. – isturdy May 02 '13 at 18:41

3 Answers3

5

My Haskell is extremely rusty but this should do it

subsortGT a, b
  | a <= b = GT
  | a > b = LT

sortGT (a1, b1) (a2, b2)
  | a1 + b1 < a2 + b2 = GT
  | a1 + b1 > a2 + b2 = LT
  | a1 + b1 ==  a2 + b2= subsortGT a1 a2

sortBy sortGT [(0,1),(0,3),(1,1),(1,3),(2,0),(2,1),(2,3),(4,1),(4,2)]
Jean-Bernard Pellerin
  • 12,556
  • 10
  • 57
  • 79
2

Leveraging tuples for sorting

import Data.Ord (comparing)
import Data.List (sortBy)

customSort = sortBy (comparing (\(x,y) -> (x+y, abs (x-y))))

or with arrows point free (point less)

import Control.Arrow ((&&&))

customSort = sortBy (comparing $ uncurry ((uncurry (&&&) .) ((+) &&& ((abs .) . subtract))))
Charles Durham
  • 1,707
  • 11
  • 17
  • Thanks for thinking about it -- it does work for the example I posted, but for this one `[(0,1),(0,3),(1,1),(1,3),(2,1),(2,2),(2,3),(4,1),(4,2)]` it outputs `[(0,1),(1,1),(2,1),(0,3),(2,2),(1,3),(2,3),(4,1),(4,2)]` where I would prefer `[(0,1),(1,1),(2,1),(2,2),(2,3),(1,3),(0,3),(4,1),(4,2)]` – גלעד ברקן May 02 '13 at 12:35
1

Try to define a custom data type and a specified order on it.
I propose you something like this,

data MPair a = MPair a a deriving (Show)

-- some helper to test
toTuple (MPair x y) = (x, y)
fromX x = MPair x x

instance (Eq a) => Eq (MPair a) where
    (MPair a b) == (MPair c d) = (a == c) && (b == d)

-- This is not exactly the ordering you are looking for
-- But at this stage it should no be a pain to define 
-- the required ordering, you just have to implement it below
instance (Ord a) => Ord (MPair a) where
    compare p@(MPair a b) q@(MPair c d)
            | p == q = EQ
            | otherwise = case (compare a c, compare b d) of  
                            (LT, _) -> LT
                            (_, LT) -> LT
                            _       -> GT

-- convert a list of tuple to a list or MPair.  
fromListToMPair :: [(a,a)] -> [MPair a]
fromListToMPair [] = []
fromListToMPair ((a, b):xs) = (MPair a b) : fromListToMPair xs

-- the inverse of the previous one   
fromMPairToList :: [MPair a] -> [(a,a)] 
fromMPairToList [] = []
fromMPairToList ((MPair a b):xs) = (a, b) : fromMPairToList xs

-- A test case
testList = [(0,1),(0,3),(1,1),(1,3),(2,0),(2,1),(2,3),(4,1),(4,2)]

Go to ghci and test it,

>>> fromMPairToList $ sort $ fromListToMPair testList
[(0,1),(0,3),(1,1),(1,3),(2,0),(2,1),(2,3),(4,1),(4,2)]
-- add a test to check the stability of your order.  
>>> fromMPairToList $ sort $ sort $ fromListToMPair testList 
[(0,1),(0,3),(1,1),(1,3),(2,0),(2,1),(2,3),(4,1),(4,2)]
-- ok this is stable

This doesn't satisfied your requirement but, it's another way that I'd like to illustrate.
In fact I've implemented the classical rule for sorting a list of tuple.
Now i'll try to define your "ordering" , then I will redefine the Ord instance for MPair. like so,

instance (Ord a, Num a) => Ord (MPair a) where  
    compare p@(MPair a b) q@(MPair c d) 
            | p == q        = EQ 
            | check a b c d = LT  
            | otherwise     = GT 
                where 
                  pred x y = (abs (x - y)) < 2
                  check a b c d = pred a c && pred b d

Then when I redo the test into ghci,

>>> fromMPairToList $ sort $ fromListToMPair testList
[(4,1),(4,2),(2,3),(2,0),(2,1),(1,3),(1,1),(0,3),(0,1)]
>>> fromMPairToList $ sort $ sort $ fromListToMPair testList
[(0,1),(0,3),(2,0),(1,1),(2,3),(1,3),(2,1),(4,1),(4,2)]
-- no this won't work, this is not an ordering, you cannot sort.  

I realize that the stability of your order is not satisfied, then sorting is not what you need.

Finally, I'd like to say it doesn't make sense as you criterion do no define an order upon your list of tuple. Your criterion is a discriminant, it will allow you to create two subgroup of data. ([criterion x is True], [criterion x is not True]). Sort your list of tuple as usual, and define a specify function (based on your criterion) which will create two distinct group.

PS : Maybe you can build an order on your data using a function based on your criterion, but I don't see how to achieve it.

zurgl
  • 1,930
  • 1
  • 14
  • 20