Reputation: 30197
[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.
Upvotes: 12
Views: 40002
Reputation: 81
I think generator is a good try:
def gen_range(start, stop, step):
while start < stop:
yield (start, min(start + step, stop))
start = start + step
if __name__ == '__main__':
print [x for x in gen_range(100, 143, 10)]
Upvotes: 1
Reputation: 284
I'd go with the generator approach, but if you're going to be unpacking the thing into an explicit list, another way to look at it is that you've got a really simple relationship between the elements of your tuple, and all you need to do is range the length properly.
def gen_range(start, stop, step):
items = ((stop-start) // step)
return [(start+(n*step), start+(n+1)*step) for n in range(items)]
The only reason I figured it's worth posting this is that my first thought (and hence the comment query) whether or not you could get away with just asking for the nth tuple in the list without having to generate the lot. If you can't, then I wouldn't go down this angle.
Upvotes: 1
Reputation: 123511
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))
Upvotes: 1
Reputation: 4111
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:])
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:])
Upvotes: 2
Reputation: 527213
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))
Upvotes: 11
Reputation: 251116
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)]
Upvotes: 1
Reputation: 6095
I'm not sure if I correctly understood the problem, but this can be simply done with a list comprehension:
start = 100
stop = 140
step = 10
[ range(s,s+step+1,step) for s in range(start,stop,step)]
if you need only to iterate over your list element one at the time, I strongly suggest you to use a generator comprehension instead:
res = ( range(s,s+step+1,step) for s in range(start,stop,step))
You can iterate over it in a cycle:
for range_piece in res:
do_something with it
Upvotes: 1