0xC0000022L
0xC0000022L

Reputation: 21319

Range between two tuples of integers?

Tuples seem ideal for version-number comparisons (assuming only numeric elements, which is a given in my case). I have two version numbers in the form of tuples comprised of integers. Say those tuples are:

minver = (1,2,3)
maxver = (1,2,9)

is there a simple and elegant way to get the "range" from minver to maxver as a list? I.e. for the above case I would like to get a list like this:

[(1,2,3), (1,2,4), (1,2,5), (1,2,6), (1,2,7), (1,2,8), (1,2,9)]

(Note: it is also fine if the last element is missing from the list, i.e. if the returned range does not include the maximum value.)

The range function clearly does not work (expects integers), but I also don't want to rely on the tuples being exactly x elements (x = 3 in the above case).

For example if I have a minver tuple (1,) it should be treated like (1,0,0) if the maxver tuple contains three values/elements (such as (1,2,3)).

Any way of doing this in a pythonic way (that is elegant)?

Upvotes: 3

Views: 2015

Answers (2)

mishik
mishik

Reputation: 10003

To make things even, it's 3:00am here :)

I think it is possible to achieve this without power ** operations, which might get expensive for big numbers.

I've written this code for python 3, but it could be easily adopted for python 2.7.

The principle is to simply generate new version until we reach the maximum one.

Here's the code:

# Easier to work with lists (for me)
min_ver = [1,1,2]
max_ver = [2,3,4]

max_num = max(min_ver + max_ver)
orig_length = len(min_ver)


def increase(an_array, max_num):
    while an_array[-1] == max_num:
        an_array.pop()
    an_array[-1] += 1
    an_array += [0] * (orig_length - len(an_array))
    return an_array


def gen_range(start, end, max_num):
    while start != end:
        yield increase(start, max_num)


for version in gen_range(min_ver, max_ver, max_num):
    print(version)

Some comparison:

Using **:

C:\Work>python -mtimeit -s"import tuples2" "tuples2.version_range((1,1,1),(1,3,5));"

10000 loops, best of 3: 101 usec per loop

Using increase:

C:\Work>python -mtimeit -s"import tuples" "tuples.gen_range([1,1,1], [1,3,5], 5)"

1000000 loops, best of 3: 0.606 usec per loop

Upvotes: 1

Jon Clements
Jon Clements

Reputation: 142216

Okay - it's 2:30am, so the principle is that you fix the maximum length and upper bound of anyone of the versions, then treat that as a base for the number... Convert your start and end to an int to act as a range, then have another function to convert back to tuple... Will need a bit of work, but fairly sound theory...

from itertools import izip_longest, chain

minver = (1, 1, 3)
maxver = (1, 3, 19)

def version_range(start, end):
    start, end = zip(*izip_longest(start, end, fillvalue=0))
    base = max(max(chain(start, end)), 9) + 1
    def _toint(seq, base):
        return sum(base ** n * val for n, val in enumerate(reversed(seq)))
    def _totuple(num, base, length):
        ret = []
        for n in (base ** i for i in reversed(range(length))):
            res, num = divmod(num, n)
            ret.append(res)
        return tuple(ret)
    for se in xrange(_toint(start, base), _toint(end, base) + 1):
        print _totuple(se, base, len(start))


version_range(minver, maxver)

Upvotes: 7

Related Questions