[python] Цикл списка из чередующихся сторон


6 Answers

cycle между получением предметов от переднего и iter и reversed . Просто убедитесь, что вы остановились на len(a) с помощью islice .

from itertools import islice, cycle

iters = cycle((iter(a), reversed(a)))
b = [next(it) for it in islice(iters, len(a))]

>>> b
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

Это можно легко поместить в одну строку, но тогда становится намного труднее прочитать:

[next(it) for it in islice(cycle((iter(a),reversed(a))),len(a))]

Помещение в одну строку также помешает вам использовать другую половину итераторов, если вы хотите:

>>> iters = cycle((iter(a), reversed(a)))
>>> [next(it) for it in islice(iters, len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]
>>> [next(it) for it in islice(iters, len(a))]
[5, 4, 6, 3, 7, 2, 8, 1, 9, 0]
Question

Учитывая список

a = [0,1,2,3,4,5,6,7,8,9]

как я могу получить

b = [0,9,1,8,2,7,3,6,4,5]

То есть, создайте новый список, в котором каждый последующий элемент поочередно берется с двух сторон исходного списка?




Две версии пока не видны:

b = list(sum(zip(a, a[::-1]), ())[:len(a)])

а также

import itertools as it

b = [a[j] for j in it.accumulate(i*(-1)**i for i in range(len(a)))]



Я бы сделал что-то подобное

a = [0,1,2,3,4,5,6,7,8,9]
b = []
i = 0
j = len(a) - 1
mid = (i + j) / 2
while i <= j:
    if i == mid and len(a) % 2 == 1:
        b.append(a[i])
        break
    b.extend([a[i], a[j]])
    i = i + 1
    j = j - 1

print b



Основным принципом вашего вопроса является так называемый алгоритм roundrobin. Страница itertools-documentation-page содержит возможную ее реализацию:

from itertools import cycle, islice

def roundrobin(*iterables):
    """This function is taken from the python documentation!
    roundrobin('ABC', 'D', 'EF') --> A D E B F C
    Recipe credited to George Sakkis"""
    pending = len(iterables)
    nexts = cycle(iter(it).__next__ for it in iterables) # next instead of __next__ for py2
    while pending:
        try:
            for next in nexts:
                yield next()
        except StopIteration:
            pending -= 1
            nexts = cycle(islice(nexts, pending))

так что все, что вам нужно сделать, это разделить ваш список на два подсписок, начиная с левого и с правого конца:

import math
mid = math.ceil(len(a)/2) # Just so that the next line doesn't need to calculate it twice

list(roundrobin(a[:mid], a[:mid-1:-1]))
# Gives you the desired result: [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

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

list(roundrobin(a, reversed(a)))[:len(a)]

или используя его как явный генератор со next :

rr = roundrobin(a, reversed(a))
[next(rr) for _ in range(len(a))]

или быстрый вариант, предложенный @Tadhg McDonald-Jensen (спасибо!):

list(islice(roundrobin(a,reversed(a)),len(a)))



Для удовольствия, вот вариант itertools:

>>> a = [0,1,2,3,4,5,6,7,8,9]
>>> list(chain.from_iterable(izip(islice(a, len(a)//2), reversed(a))))
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

Это работает там, где len(a) четное. Для этого потребуется специальный код для ввода с нечетным удлинением.

Наслаждайтесь!




Не уверен, может ли это быть написано более компактно, но оно эффективно, поскольку оно использует только итераторы / генераторы

a = [0,1,2,3,4,5,6,7,8,9]

iter1 = iter(a)
iter2 = reversed(a)
b = [item for n, item in enumerate(
        next(iter) for _ in a for iter in (iter1, iter2)
    ) if n < len(a)]



Вы можете просто pop вперед и назад:

b = [a.pop(-1 if i%2 else 0) for i in range(len(a))]

Примечание. Это уничтожает исходный список, a .




Related