[edit] I'm not sure if this better off to codereview, if so, please migrate :) thanks!
So we were sitting here, working on a semi-academic problem.
Given a start
, stop
and step
, generate a list of range tuples, so that
gen_range(100, 140, 10)
would produce
[(100, 110), (110, 120), (120, 130), (130, 140)]
also, given then fact that it should work on, say, iterating 500M integers with a step of 100 and not take forever.
The implementation I've come up with is as follows:
def gen_range(start, stop, step):
llist = range(start, stop+step, step)
batch_list = []
if llist[-1] > stop:
llist[-1] = stop
for a, b in enumerate(llist[:-1]):
batch_list.append((llist[a], llist[a+1]))
print batch_list
gen_range(100000000,600000000,100)
But I can't stop thinking that it can be done in a more efficient (also code-length wise) way. Any suggestions?
[edit]
One thing I forgot to point out. If the range boundary is not equal to the step, i.e. you have a case of:
gen_range(100, 143, 10)
the upper boundary should be 143, and not 150, as some answers here would produce, due to range()
internals.
Maybe code-length wise if (b - a) % c == 0
:
def gen_range(a, b, c):
return [(i, i + c) for i in range(a, b, c)]
or
def gen_range_2(a, b, c):
s = range(a, b + c, c)
return zip(s, s[1:])
See http://ideone.com/VhY215
If (b - a) % c != 0
:
def gen_range(a, b, c):
return [(i, i + c) for i in range(a, b - ((b - a) % c), c)]
or
def gen_range_2(a, b, c):
s = range(a, b - ((b - a) % c) + c, c)
return zip(s, s[1:])
See http://ideone.com/i3Pq69
ranges = [(n, min(n+step, stop)) for n in xrange(start, stop, step)]
Perhaps via a generator?
def gen_range(start, stop, step):
current = start
while current < stop:
next_current = current + step
if next_current < stop:
yield (current, next_current)
else:
yield (current, stop)
current = next_current
Calling this function gives you a generator object that will produce each of the tuples in order. You would use it like this:
for block in gen_range(100000000,600000000,100):
print block
which would output...
(100000000,100000100)
(100000100,100000200)
(100000200,100000300)
...
(599999900,600000000)
You could do this even more simply with a generator expression if you're always certain that stop-start
is an even multiple of the step
:
ranges = ((n, n+step) for n in xrange(start, stop, step))
# Usage
for block in ranges:
print block
Also note that if you want to turn a generator into a list, holding the entirety of the results in memory, you can simply pass it to list()
:
all_ranges = list(gen_range(100000000,600000000,100))
something like this:
In [48]: it=iter(xrange(100,200,10))
In [49]: it1=iter(xrange(110,210,10))
In [50]: [(next(it),next(it1)) for _ in range(10)]
Out[50]:
[(100, 110),
(110, 120),
(120, 130),
(130, 140),
(140, 150),
(150, 160),
(160, 170),
(170, 180),
(180, 190),
(190, 200)]
or using zip()
:
In [55]: it=iter(xrange(100,200,10))
In [56]: it1=iter(xrange(110,210,10))
In [57]: [(x,y) for x,y in zip(it,it1)]
Out[57]:
[(100, 110),
(110, 120),
(120, 130),
(130, 140),
(140, 150),
(150, 160),
(160, 170),
(170, 180),
(180, 190),
(190, 200)]
I'd make it a generator so it could handle huge ranges.
def gen_range(start, stop, step):
a, b = start, start+step
while b <= stop:
yield a, b
a, b = b, b+step
print list(gen_range(100, 140, 10))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With