Понимание Python itertools.chain и следующего

Even A. Rognlien спросил: 28 марта 2018 в 04:11 в: python

Я пытаюсь преобразовать строку кода Python в JavaScript, но у меня есть ограниченное знание Python и пытаюсь понять это.

Может кто-нибудь попытаться объяснить следующую строку кода? Функция point_orientation не важна, она просто возвращает True / False.

i_extend = next( ( i for i in itertools.chain(range(i_start+1, len(p)), range(0,i_start+1)) if not point_orientation( p[i-1], p[i], p[(i+1) % len(p)] ) ) )

3 ответа

Alex Hall ответил: 28 марта 2018 в 04:34

Это означает "найти первый элемент i в range(i_start+1, len(p)) или range(0,i_start+1) (если в первом диапазоне его нет), так что point_orientation( p[i-1], p[i], p[(i+1) % len(p)] ) ложно". Если такого i не существует, оно вызовет исключение.

Здесь более подробный Python:

def check(i):
    return point_orientation(p[i - 1],
                             p[i],
                             p[(i + 1) % len(p)])def find_i_extend():
    for i in range(i_start + 1, len(p)):
        if not check(i):
            return i    for i in range(0, i_start + 1):
        if not check(i):
            return ii_extend = find_i_extend()
M. I. Wright ответил: 28 марта 2018 в 04:43

Здесь itertools.chain просто создает список из обоих диапазонов, который может быть лучше выражен в этом контексте как (*range(i_start+1, len(p)), *range(i_start+1)). (Распаковка: https://codeyarns.com/2012/04/26/unpack-operator-in-python/)

next() просто получает "следующий" элемент итерируемого и здесь, поскольку он работает непосредственно с выражением генератора, он служит для получения первого элемента; next(iter([1, 2, 3])), аналогично, вернет 1.

Это может быть написано более идиоматически, если это поможет -

next(i for i in (*range(i_start+1, len(p)), *range(i_start+1)) if not point_orientation(p[i-1], p[i], p[(i+1) % len(p)]))

- или, если next() все еще доставляет вам проблемы, вы можете вместо этого просмотреть его (обратите внимание на [0] в конце):

[i for i in (*range(i_start+1, len(p)), *range(i_start+1)) if not point_orientation(p[i-1], p[i], p[(i+1) % len(p)])][0]
Alex Hall ответил: 28 марта 2018 в 04:28
Код разумен - использование next и chain означает, что структуры данных не должны сохраняться в памяти. (*range(i_start+1, len(p)), *range(i_start+1)) создает кортеж.
tdelaney ответил: 28 марта 2018 в 04:40
Смысл этого кода состоит в том, чтобы проверить несколько диапазонов по одному критерию, найти первое совпадение и прекратить дальнейшие вычисления.
M. I. Wright ответил: 28 марта 2018 в 04:42
@AlexHall, если честно, я как-то совсем забыл, что chain() возвращает итератор, а не список. (Также оплакивал пробелы, окружающие аргументы вызова функции) - заметьте, что я не рекомендовал использование listcomp над next(), так как последний пример состоял в том, чтобы просто ввести next() перспектива для ОП.
tdelaney ответил: 28 марта 2018 в 04:43
Пока мы говорим о Python 3.x, chain и range являются итераторами. Они не должны быть расширены до кортежа, потому что смысл состоит в том, чтобы остановить итерацию в первом совпадении и не расширять все.
chepner ответил: 28 марта 2018 в 06:47

chain объединяет два итератора в один. Здесь он используется, чтобы помочь имитировать замкнутый цикл, начинающийся в определенной точке. Обратите внимание, что

range(0, len(p)) == chain(range(0, i_start+1), range(i_start+1, len(p))

Данный код меняет местами аргументы в chain, так что он преобразует последовательность типа [0,1,2,...,10] в последовательность типа . Три аргумента предиката являются просто смежными элементами в цикле, а [5,6,...,10,0,1,...,4] выполняет обтекание в конце последовательности. (i+1) % len(p) просто используется для получения первого элемента результирующей последовательности.

Если вы отказались от попыток выразить это в виде одной строки, вы можете написать более понятный код, который следует:

n = len(p)
for i in range(i_start + 1, i_start + 1 + n):
    index_before = (i - 1) % n
    index_at = i % n
    index_after = (i+1) % n
    if not point_orientation(p[index_before], p[index_at], p[index_after])
        i_extend = y
        break

Мы выполняем итерацию по одному диапазону и выполняем модульную арифметику для каждого индекса в цикле. Если предикат проходит, мы устанавливаем next в текущую точку и вырываемся. В противном случае мы продолжим итерацию, предполагая, что одна из точек в конечном итоге будет успешной.