Reputation: 76788
I need to search for an item in a list around a given index with in a given radius. Currently I use this function to generate alternating offsets for the search:
def generateSearchIndizes(radius):
for i in range(1, radius + 1):
yield i
yield -i
The code that does the search looks something like this:
for i in generateSearchIndizes():
if pred(myList[baseIndex + i]):
result = myList[baseIndex + i]
break # terminate search when first item is found
My question is, is there a more elegant way to generate the search indizes, maybe without defining a special function?
Upvotes: 6
Views: 1424
Reputation: 95921
Your own solution, adding a yield 0
at the start of the generator, is the most straightforward (aka pythonic).
Here's an infinite offset generator with a different algorithm:
def gen_offsets():
offset= 0
yield offset
step= 1; sign= 1
while 1:
offset+= sign*step
yield offset
step+= 1; sign= -sign
A more fanciful (aka not-so-pythonic :) way to write the algorithm above is:
import itertools as it, operator as op
def gen_offsets():
steps= it.imap(op.mul, it.count(1), it.cycle( (1, -1) ))
offset= 0
yield offset
for step in steps:
offset+= step
yield offset
Upvotes: 0
Reputation: 123453
This appears to me at least as readable as a separate function -- and arguably more understandable:
radius = 3
for outerbounds in ((-r,r) for r in range(1,radius+1)):
for i in outerbounds :
print i
# -1
# 1
# -2
# 2
# -3
# 3
Upvotes: 0
Reputation: 63709
Why alternate -i, i? Just do:
for i in range(-radius, radius+1):
listitem = myList[baseIndex + i]
if pred(listitem):
return listitem
Or if you absolutely must approach from front and back to get the outermost pred-matcher, how about:
for i in sorted(range(-radius, radius+1), key=abs):
listitem = myList[baseIndex + i]
if pred(listitem):
return listitem
If you have to do this often, just build sorted(range(-radius,radius+1),key=abs)
once and keep it around for future iterating.
If you absolutely must not use the 0'th element, just insert a if not i: continue
at the start of your loop.
Upvotes: 0
Reputation: 63709
Why not just do this with an inner loop, instead of creating a funky generator:
found = False
for i_abs in range(1, radius+1):
for i in (i_abs, -i_abs):
listitem = myList[baseIndex + i]
if pred(listitem):
result = listitem
found = True
break # terminate search when first item is found
if found:
break
else:
# handling in case no match found for pred
Some other comments:
you never test the 0'th element
since you are testing from both left and right, you should stop after i_abs reaches the halfway mark
I don't like repeating list indexing operations,
would rather repeat a variable reference; so I
lifted out the repeated myList[baseIndex+i]
and
assigned it to listitem
you should add some handling in case there is no matching element found
instead of breaking from the inner loop (which
here requires an extra found
variable to
break out of the outer for loop), you might
be better just returning result
right from the
inner loop,
as in:
for i_abs in range(1, radius+1):
for i in (i_abs, -i_abs):
listitem = myList[baseIndex + i]
if pred(listitem):
return listitem
Then there is no break management or found
variable required.
Upvotes: 0
Reputation: 26873
Here's my go at it:
from itertools import chain
>>> list(chain(*zip(range(1, 7), range(-7, 0)[::-1])))
[1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6]
Adjust as needed. :)
Upvotes: 2
Reputation: 72241
Perhaps a generator expression?
for i in (x/2 * (x%2 * 2 - 1) for x in xrange(2, radius*2)):
print i
It is short and fits to your "without defining a special function" requirement...
But, franky, I'd still prefer to use that special function instead - just for the sake of having more clear code. :)
Upvotes: 0
Reputation: 838156
is there a more elegant way to generate the search indices
I don't think there's a more elegant way. Your code is very simple and clear.
maybe without defining a special function?
Yes, that's definitely possible.
>>> [b for a in ((x,-x) for x in range(1, 10 + 1)) for b in a]
[1, -1, 2, -2, 3, -3, 4, -4, 5, -5, 6, -6, 7, -7, 8, -8, 9, -9, 10, -10]
Upvotes: 4