简体   繁体   English

如何产生交替范围?

[英]How to generate an alternating range?

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? 我的问题是,是否有一种更优雅的方式来生成搜索索引,也许无需定义特殊功能?

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]

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. 根据需要进行调整。 :) :)

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. 但是,坦率地说,我仍然更喜欢使用该特殊功能-只是为了拥有更清晰的代码。 :) :)

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 您永远不会测试第0个元素

  • since you are testing from both left and right, you should stop after i_abs reaches the halfway mark 由于您是从左右两个方向进行测试,因此您应该在i_abs达到一半时停止

  • 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 所以我提出了重复的myList[baseIndex+i]并将其分配给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, 与其从内部循环中断开(这里需要一个额外的found变量才能从外部for循环中断开),不如直接从内部循环中返回result

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. 这样就不需要中断管理或found所需的变量。

Why alternate -i, 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: 或者,如果您绝对必须从正面和背面接近才能获得最外面的pred-matcher,该如何做:

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. 如果您必须经常执行此操作,则只需构建一次sorted(range(-radius,radius+1),key=abs)即可,以备将来使用。

If you absolutely must not use the 0'th element, just insert a if not i: continue at the start of your loop. 如果您绝对不能使用第0个元素,则只需插入一个if not i: continue在循环开始处if not i: continue

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

Your own solution, adding a yield 0 at the start of the generator, is the most straightforward (aka pythonic). 您自己的解决方案(在生成器的开头添加yield 0是最简单的(也就是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: 编写上述算法的一种更奇特的方法(又名非Python的:):

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM