如何从两端迭代一个序列?

How to iterate a sequence from both ends?

我想迭代(部分)Python 序列 "from both ends"(到(那部分的)中间,在这里)。
在 C/C++/Java 中,我可能会尝试

int start = 2, beyond = 7;
for (int forward = start, back = beyond ; forward <= --back ; forward++)
    printf("%d %d\n", forward, back);

我似乎无法决定我最不喜欢以下哪个变体:

seq = tuple(chr(c) for c in range(ord('a'), ord('z')+1))
start, beyond = 2, 7

print("subtract")
for forward in range(start, beyond):
    back = start + beyond - 1 - forward
    if (back < forward):
        break
    print(seq[forward], seq[back])
#    if (back <= forward): XXX ERROR, as YvesgereY pointed out
#        break
print("reversed")
for forward, back in zip(range(start, beyond),
                         reversed(range(start, beyond))):
    if (back < forward):
        break
    print(seq[forward], seq[back])
print("step -1")
for forward, back in zip(range(start, beyond), range(beyond-1, start-1, -1)):
    if (back < forward):
        break
    print(seq[forward], seq[back])
print("naked code")
forward, back = start, beyond-1 # thanks again, YvesgereY
while (forward <= back):
    print(seq[forward], seq[back])
    forward += 1
    back -= 1

是否有 pleasing/sane/idiomatic 方法来处理这个问题?

我宁愿写:

size = max((beyond-start+1)//2,0)  # 0 if start>beyond
for i in range(0,size):
   print(start+i,beyond-i-1)

请注意,您的 substractstep -1 版本不正确,因为它们在上一次迭代中允许 forward>back

您可以通过强调循环条件(降低差一错误的可能性)来稍微改进 while 版本:

forward, back = start, beyond-1
while forward <= back:
    print(forward, back)
    forward += 1
    back -= 1

在我看来,最后一种使用while循环的方法是最好的:

forward, back = start, beyond
while (forward < back):
    back -= 1
    print(forward, back)
    forward += 1

它简洁但仍然可读,语义清晰,与其他具有多层函数调用嵌套的方法相比,似乎是最'Pythonic'。您可以通过将其包装在生成器中来改进它:

def iterate_to_middle(start_front, start_end):
    while start_front < start_end:
        start_end -= 1
        yield start_front, start_end
        start_front += 1

然后可以这样使用:

>>> for indexes in iterate_to_middle(2, 7):
    print(indexes)


(2, 6)
(3, 5)
(4, 4)

你可能想多了。 while 循环没问题。

此外,Python 中的序列类型支持 负索引 ,我认为这可以简化事情。这是一种使用 enumerate 为每个前向项生成负对应索引的方法:

lst = [1, 2, 3, 4, 5]

for i, x in enumerate(lst, 1):
    print(x, lst[-i])
    if i * 2 >= len(lst): break

(1, 5)
(2, 4)
(3, 3)

为什么不 zip 列表本身的反面?

mid = (len(lst) + 1) // 2   
for x, y in zip(lst[:mid], lst[::-1]):
     print((x, y))

(1, 5)
(2, 4)
(3, 3)

zip 自动处理不同长度的列表,只要确保你正确设置中点(这取决于列表大小是否均匀)。

您可以使用 ~ 运算符得到 (index + 1) * -1。

arr = [1, 2, 3, 4, 5]
for i in range((len(arr) + 1) // 2):
    print(arr[i], arr[~i])

1 5
2 4
3 3