views:

175

answers:

6

I have a function that is passed two values and then iterates over the range of those values. The values can be passed in any order, so I need to find which one is the lowest first. I had the function written like this:

def myFunc(x, y):
    if x > y:
        min_val, max_val = y, x
    else:
        min_val, max_val = x, y
    for i in range(min_val, max_val):
    ...

But to save some screen space, I ended up changing it to:

def myFunc(x, y):
   min_val, max_val = sorted([x, y])
   for i in range(min_val, max_val):
   ...

How bad is this? Is there a better way that's still one line?

+5  A: 

min and max are your friends.

def myFunc(x, y):
    min_val, max_val = min(x, y), max(x, y)

Edit. Benchmarked min-max version againt a simple if. Due to the function call overhead, min-max takes 2.5x longer that the simple if; see http://gist.github.com/571049

The MYYN
I hadn't even thought of doing that in one line. But depending on how sorted is implemented, it might actually be less efficient. I know the standard library sort in C++ is hard-coded for <7 elements, so sorting a two element list would just be a single comparison. There is the added cost of creating a list object, though.
Colin
+4  A: 

I like the sorted one. Clever but not too clever. Here are some other options.

def myFunc(min, max):
    if min > max: min, max = max, min

def myFunc(x, y):
    min, max = min(x, y), max(x, y)

def myFunc(x, y):
    min, max = [f(x, y) for f in (min, max)]

The last one's a bit silly I admit.

John Kugelman
It's best to use other names than min and max, as they are Python built-ins.
John Y
+1  A: 

Some suggestions

def myfunc(minVal, maxVal):
    if minVal > maxVal: minVal, maxVal = maxVal, minVal

def myfunc2(a, b):
    minVal, maxVal = ((a, b), (b, a))[a > b] # :-P

Using sorted, the min/max builtins or the second solution above seems overkill in this case.

And remember that range(min, max) will iterate from min to max - 1!

AndiDog
+3  A: 

Unless you need to microoptimise, I'd just to this

def myFunc(x, y):
    for i in range(*sorted((x, y))):
        ...

This is faster though

def myFunc(x, y):
    for i in range(x,y) if x<y else range(y,x):
        ...

minmax.py

def f1(x, y):
    for i in range(min(x, y), max(x, y)):
        pass

def f2(x, y):
    for i in range(*sorted((x, y))):
        pass

def f3(x, y):
    for i in range(x, y) if x<y else range(y, x):
        pass

def f4(x, y):
    if x>y:
        x,y = y,x
    for i in range(x, y):
        pass

def f5(x, y):
    mn,mx = ((x, y), (y, x))[x>y]
    for i in range(x,y):
        pass

benchmarks (f3 is fastest regardless of the order)

$ python -m timeit -s"import minmax as mm" "mm.f1(1,2)"
1000000 loops, best of 3: 1.93 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f2(1,2)"
100000 loops, best of 3: 2.4 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f3(1,2)"
1000000 loops, best of 3: 1.16 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f4(1,2)"
100000 loops, best of 3: 1.2 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f5(1,2)"
1000000 loops, best of 3: 1.58 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f1(2,1)"
100000 loops, best of 3: 1.88 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f2(2,1)"
100000 loops, best of 3: 2.39 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f3(2,1)"
1000000 loops, best of 3: 1.18 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f4(2,1)"
1000000 loops, best of 3: 1.25 usec per loop
$ python -m timeit -s"import minmax as mm" "mm.f5(2,1)"
1000000 loops, best of 3: 1.44 usec per loop
gnibbler
A: 

The single best answer works like:

def foo(lo, hi):
    if hi < lo: lo,hi = hi,lo

It's clear, makes a point, and doesn't obscure meaning in a bunch of extra glue. It's short. It's almost certainly as fast as any other option in practice, and it relies on the least amount of cleverness.

Ian
+1  A: 

Since the OP's question was posed using x and y as parameters (not lo and hi), I would go with (for both speed and clarity):

def myfunc(x, y):
    lo, hi = (x, y) if x < y else (y, x)

>>> timeit.repeat("myfunc(10, 5)", "from __main__ import myfunc")
[1.2527812156004074, 1.185214249195269, 1.1886092749118689]
>>> timeit.repeat("foo(10, 5)", "from __main__ import foo")
[1.0397177348022524, 0.9580022495574667, 0.9673979369035806]
>>> timeit.repeat("f3(10, 5)", "from __main__ import f3")
[2.47303065772212, 2.4192818561823515, 2.4132735135754046]
Don O'Donnell