I have been thinking about order operations and generalized * in Python recently. I want to share some of my thinking here, even the topic is quite theoretical, considering the beauty and orthogonality of the language.
Point one
You can do comparison with tuples of numbers in Python

>>> (0,0)<(1,1)<(2,2)
True
>>> (0,0)<(1,1)<(2,0)
True
>>>

First is fine for me, but I am not agreeing with the second one. Why?

If a<b<c, then number b is between a and c in the number line.

(x,y) is point in two dimensional plane. For me the natural extension of < is for it to mean that

(a,b) < (c,d) < (e,f)

means that (c,d) is inside a square whose corners are (a,b) and (e,f). That is same as

a<c<e and b<d<f

Point two

There is + generalized for many types, but I think it would be nice and logical that there would be opposite -:

>>> (0,1) * 2
(0, 1, 0, 1)
>>> _ / 2

Traceback (most recent call last):
  File "<pyshell#3>", line 1, in <module>
    _ / 2
TypeError: unsupported operand type(s) for /: 'tuple' and 'int'
>>> 'abcd'+'ef'
'abcdef'
>>> _ - 'ef'

Traceback (most recent call last):
  File "<pyshell#5>", line 1, in <module>
    _ - 'ef'
TypeError: unsupported operand type(s) for -: 'str' and 'str'
>>>

By logic /2 should mean half of sequence and - should mean removing the sequence from the end of first sequence.

What you think? Could there be easy way to program these things to function?

Recommended Answers

All 10 Replies

Tuples aren't necessarily points logically.
When comparing lesser than or greater than with tuples, the first element is compared.

Point one
You can do comparison with tuples of numbers in Python

>>> (0,0)<(1,1)<(2,2)
True
>>> (0,0)<(1,1)<(2,0)
True
>>>

First is fine for me, but I am not agreeing with the second one. Why?

If a<b<c, then number b is between a and c in the number line.

(x,y) is point in two dimensional plane. For me the natural extension of < is for it to mean that

(a,b) < (c,d) < (e,f)

means that (c,d) is inside a square whose corners are (a,b) and (e,f). That is same as

a<c<e and b<d<f

The problem with your 'natural extension' of < to points in the plane is that you are defining a ternary relation P < Q < R and not a binary relation P < Q. The 'less than' relation is binary. Mathematically, it is an order relation (see http://en.wikipedia.org/wiki/Order_theory) which satisfies (much more) natural properties that you would expect from an ordering relation, for example P < Q and Q < R imply that P < R. In python (and also in mathematics), a < b < c means (a < b and b < c).

Python's ordering for tuples is called lexicographic order in mathematics. It is consistent with the usual ordering of words, for example tuple("hello") < tuple("world") , and it is also commonly used to order points in the plane. I'm afraid that your ternary relation can not be used to build a coherent binary relation.

commented: good explanation +10

> By logic /2 should mean half of sequence

Only if those halves are identical. What would be a result of

(0, 1, 2, 3) / 2

What Girbouillis said.

In addition, we have evidence that defining operators sometimes leads to brittle or even chaotic programs: C++ allows operators to be overloaded, both for your own types and others. The results, when done very carefully are both useful and powerful, but when done without enough thought about (unintended) consequences, they have been widely seen as a source of trouble. In fact one of the reasons given that Java has no user defined operators is just that C++ has showed how dangerous they are.

It is important to obey the law of least astonishment when working with the basics, such as operators, of a language. I could go so far as to say (I won't: Convenience has some merit also) that it was a mistake to define operator * for (string,integer) and (integer,string) because if "aa"*2 works, I'm (mildly) 'astonished' that "aa"*1.5 fails as does "aaaa"/2 ; and why isn't "ab"*"cd" == "acadbcbd" ? Similarly, since operator + is defined for (string,string), I might expect that "aabb" - "bb" should yield "aa" .

Python has no problem accepting by binary definition of my relationship, but maybe it is not really < but between, even between is little strange name also for two variables, it should be mayby lower_dimension_or_all_less_or_equal but that is little unconvenient name to type correctly many times.

It does have weakness that it is both neither lexinographic nor numeric order for letter sequences.

def order(a,b):
    try:
        return len(b)>len(a) or all(tuple(a[i]<=b[i] for i in range(len(a))) )
    except:
        return b>=a  ## no len i.e. zero dimensions

for i in ((( 0,0,0),(1,1,1) ),
          ((0,0),(1,1)),  ## (0,0)<=(1,1)<=(2,2)
          ((1,1),(2,2)),  ## both True => desired result
          ((0,0),(1,1)),  ## (0,0) <= (1,1) <= (2,0)
          ((1,1),(2,0)),  ## gives False => desired result
          ('ab','cd'),
          ('ab','aa'),
          ('ab','abc'),
          ('ab','b'),  ## different than normal <
          ('aac','ada'), ## different than normal <
          ('10','12'),
          (10,100),
          ('10','100')):
    print i,order(*i)

Interesting, of course you can make your own object inheriting from tuple like this:

class TonyjvTuple(tuple):

    def __new__(cls,iterable):
        return tuple.__new__(cls,iterable)

    def __str__(self):
        return "Tonyjv style tuple ({0}).".format(
            ",".join([str(item) for item in self]))
    
    def __lt__(self,other): #Less than
        return True  #Silly; TODO: implement functionality
        
    def __le__(self,other): #Less or equal
        return True  #Silly; TODO: implement functionality
        
    def __gt__(self,other): #Greater than
        return True  #Silly; TODO: implement functionality
        
    def __ge__(self,other): #Greater or equal
        return True  #Silly; TODO: implement functionality


a = TonyjvTuple((1,2))

print a

b = TonyjvTuple((2,5))

if b > a:
    print("Of course that's true.")

if a > b:
    print("What? Breakdown of logic!")

#OUTPUT:
    #Tonyjv style tuple (1,2).
    #Of course that's true.
    #What? Breakdown of logic!

Python has no problem accepting by binary definition of my relationship, but maybe it is not really < but between, even between is little strange name also for two variables, it should be mayby lower_dimension_or_all_less_or_equal but that is little unconvenient name to type correctly many times.

It does have weakness that it is both neither lexinographic nor numeric order for letter sequences.

def order(a,b):
    try:
        return len(b)>len(a) or all(tuple(a[i]<=b[i] for i in range(len(a))) )
    except:
        return b>=a  ## no len i.e. zero dimensions

for i in ((( 0,0,0),(1,1,1) ),
          ((0,0),(1,1)),  ## (0,0)<=(1,1)<=(2,2)
          ((1,1),(2,2)),  ## both True => desired result
          ((0,0),(1,1)),  ## (0,0) <= (1,1) <= (2,0)
          ((1,1),(2,0)),  ## gives False => desired result
          ('ab','cd'),
          ('ab','aa'),
          ('ab','abc'),
          ('ab','b'),  ## different than normal <
          ('aac','ada'), ## different than normal <
          ('10','12'),
          (10,100),
          ('10','100')):
    print i,order(*i)

All right, you built a binary order relation, but it's only a partial order:

def order(a,b):
    try:
        return len(b)>len(a) or all(tuple(a[i]<=b[i] for i in range(len(a))) )
    except:
        return b>=a  ## no len i.e. zero dimensions

for i in (
    ("ab", "ba"), # False
    ("ba", "ab"), # False
    ):
    print i, order(*i)

The drawbacks of this are that you can't easily use it to sort elements, also that this relation is probably seldom useful and worse, if you define <= like this, you must give a new name to lexicographic order (which is very useful, for exemple in dictionaries).

The comment of the scalar case is not good, it would equal more to 1 dimensional singleton.

Yes my relationship does not give order both ways for some numbers.

It is however order for complex numbers in some cases, but now they have never order.

I tried little different definition, but this strong ordering gives quite difficult results in mixed types situation (ala ''>False in Python 2.6)

def order(a,b):
    try:
        return len(b)>len(a) or all(tuple(a[i]<=b[i] for i in range(len(a))) )
    except:
        return order((a,),(b,))  ## no len -> one dimensions

for i in (('ab','ba'),
          ('ba','ab'),
          (('',''),'a'),
          ('13','22'),
          (10,(0,0)),
          ('a',('a',)),
          (('a',),'a')):
    print 'order(%s) = %s ' %(i, order(*i))
"""Output:
order(('ab', 'ba')) = False 
order(('ba', 'ab')) = False 
order((('', ''), 'a')) = False 
order(('13', '22')) = False 
order((10, (0, 0))) = True 
order(('a', ('a',))) = True 
order((('a',), 'a')) = True
"""

Looks like we should raise error if compared things are not of same type (like Python 3 maybe does).

(0, 1, 2, 3) / 2

should be (0,1) but of course then:
(0, 1, 2, 3) / 2 *2 -> (0, 1, 0, 1) ## different
2* (0,1,2,3) /2 -> ((0,1,2,3) ## succeed

I think if I would generalize the order in higher dimensions, there is more orders than simply between values, basically all 9 (center and all neighbours of square) of them for two dimensions and so on. So I would need to return number indicating the square where the point is in four directions (like coordinate axis sectors:

-/+       +/+

         .

   -/-       +/-

if firs comparison is +/+, and second -/- then it fulfils is in the box relationship I talked about. Maybe should also have to equalities: equal_x and equal_y, in case both are true, points would be same.

Normal comparision would be like this (number line ie. one axis):

-          .           +

for x and y and x<y = True means that y is in x's + and y is in x's - side.

of course there is the . also which means equal (0).

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.