Попарный обход списка или кортежа

a = [5, 66, 7, 8, 9, ...]

Возможно ли сделать итерацию вместо записи следующим образом?

a[1] - a[0]

a[2] - a[1]

a[3] - a[2]

a[4] - a[3]

...

Спасибо!

Ответ 1

Конечно.

for i in range(1, len(a)):
    print a[i] - a[i-1]

Я не вижу, что такое настоящая проблема. Вы читали учебник python?

Ответ 2

Использование range прекрасно. Тем не менее, программирование (как математика) о построении на абстракциях. Последовательные пары [(x0, x1), (x1, x2),..., (xn-2, xn-1)], называются парными комбинациями, см., Например, рецепт в документах itertools. Если у вас есть эта функция в вашем наборе инструментов, вы можете написать:

for x, y in pairwise(xs):
    print(y - x)

Или, как выражение генератора:

consecutive_diffs = (y - x for (x, y) in pairwise(xs))

Ответ 3

для небольшого списка в python 2 или в любом списке в python 3, вы можете использовать

[x - y for x, y in zip(a[1:], a)]

для большего списка, вы, вероятно, захотите

import itertools as it

[x - y for x, y in it.izip(a[1:], a)]

если вы используете python 2

И я бы подумал записать его как выражение генератора вместо

(x - y for x, y in it.izip(a[1:], a))

Это позволит избежать одновременного создания второго списка в памяти, но вы сможете выполнять только один раз. Если вы хотите только перебирать его один раз, то это идеальный вариант, и его достаточно легко изменить, если позже вы решите, что вам нужен случайный или повторный доступ. В частности, если вы собираетесь его обработать, чтобы составить список, то этот последний вариант идеален.

обновление:

Самый быстрый способ -

import itertools as it
import operator as op

list(it.starmap(op.sub, it.izip(a[1:], a)))

$ python -mtimeit -s = [1, 2]*10000' '[x - y for x, y in zip(s[1:], s)]'
100 loops, best of 3: 13.5 msec per loop

$ python -mtimeit -s'import itertools as it; s = [1, 2]*10000' '[x - y for x, y in it.izip(s[1:], s)]'
100 loops, best of 3: 8.4 msec per loop

$ python -mtimeit -s'import itertools as it; import operator as op; s = [1, 2]*10000' 'list(it.starmap(op.sub, it.izip(s[1:], s)))'
100 loops, best of 3: 6.38 msec per loop

Ответ 4

Вот пример из recitpes itertools:

from itertools import tee

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return zip(a, b)

Это не очень читаемо. Если вы предпочитаете что-то более понятное и понять, как работают генераторы, здесь немного более длинный пример с тем же результатом:

def pairwise(it):
    """
    Walk a list in overlapping pairs.

    >>> list(pairwise([0, 1, 2, 3, 4, 5]))
    [(0, 1), (1, 2), (2, 3), (3, 4), (4, 5)]
    """
    it = iter(it)
    start = None
    while True:
        if not start:
            start = next(it)
        end = next(it)
        yield start, end
        start = end

Ответ 5

def pairwise(iterable):
    i = iter(iterable)
    while True:
        yield next(i), next(i, '')

Ответ 6

Я бы порекомендовал использовать удивительную библиотеку more_itertools, она имеет готовую к использованию попарную функцию:

import more_itertools

for a, b in more_itertools.pairwise([1, 2, 3, 4, 5]):
    print(a, b)
# 1 2
# 2 3
# 3 4
# 4 5

Это избавит вас от написания собственной (вероятно, глючной) реализации. Например, большинство реализаций на этой странице не обрабатывают случай с пустой итерацией правильно - функция генератора не должна вызывать StopIteration, такое поведение считается устаревшим и вызывает DeprecationWarning в Python 3.6. Это не будет работать в Python 3.7 вообще.