Подтвердить что ты не робот

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

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

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

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

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

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

4b9b3361

Ответ 1

>>> [a[-i//2] if i % 2 else a[i//2] for i in range(len(a))]
[0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

Объяснение:
Этот код выбирает числа из начала (a[i//2]) и из конца (a[-i//2]) of a, чередуясь (if i%2 else). Общее количество len(a) выбрано, поэтому это не дает никаких негативных последствий, даже если len(a) является нечетным.
[-i//2 for i in range(len(a))] дает 0, -1, -1, -2, -2, -3, -3, -4, -4, -5,
[ i//2 for i in range(len(a))] дает 0, 0, 1, 1, 2, 2, 3, 3, 4, 4,
и i%2 чередуется между False и True,
поэтому индексы, которые мы извлекаем из a, равны: 0, -1, 1, -2, 2, -3, 3, -4, 4, -5.

Моя оценка питоничности:
Самое приятное в этом однострочном лайнере состоит в том, что оно короткое и показывает симметрию (+i//2 и -i//2).
Плохая вещь, однако, в том, что эта симметрия обманчива:
Можно подумать, что -i//2 были такими же, как i//2, когда знак перевернулся. Но в Python целочисленное деление возвращает пол результата вместо усечения в нуль. Итак, -1//2 == -1.
Кроме того, я нахожу доступ к элементам списка по индексу менее pythonic, чем итерация.

Ответ 2

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]

Ответ 3

Очень хороший однострочный слой в Python 2.7:

results = list(sum(zip(a, reversed(a))[:len(a)/2], ()))
>>>> [0, 9, 1, 8, 2, 7, 3, 6, 4, 5]

Сначала вы закроете список своим обратным, возьмите половину этого списка, суммируйте кортежи, чтобы сформировать один кортеж, а затем конвертируйте в список.

В Python 3, zip возвращает генератор, поэтому вам нужно использовать islice из itertools:

from itertools import islice
results = list(sum(islice(zip(a, reversed(a)),0,int(len(a)/2)),()))

Edit: Похоже, что это работает отлично только для длин четного списка - длины нечетного списка будут опускать средний элемент:( Небольшая поправка для int(len(a)/2) to int(len(a)/2) + 1 даст вам дублирующее среднее значение, поэтому будьте предупреждены.

Ответ 4

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

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

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

Ответ 5

Для удовольствия, вот вариант 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) четное. Для этого потребуется специальный код для нечетного ввода.

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

Ответ 6

Не сильно отличается от некоторых других ответов, но он избегает условного выражения для определения знака индекса.

a = range(10)
b = [a[i // (2*(-1)**(i&1))] for i in a]

i & 1 чередуется между 0 и 1. Это заставляет экспонента чередоваться между 1 и -1. Это приводит к тому, что делитель индекса чередуется между 2 и -2, что приводит к тому, что индекс чередуется от конца к концу по мере увеличения i. Последовательность a[0], a[-1], a[1], a[-2], a[2], a[-3] и т.д.

(я повторяю i над a, так как в этом случае каждое значение a равно его индексу. В общем случае итерация по range(len(a)).)

Ответ 7

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

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)))

Ответ 8

Используйте правую toolz.

from toolz import interleave, take

b = list(take(len(a), interleave((a, reversed(a)))))

Во-первых, я попробовал нечто похожее на решение Раймонда Хеттингера с itertools (Python 3).

from itertools import chain, islice

interleaved = chain.from_iterable(zip(a, reversed(a)))
b = list(islice(interleaved, len(a)))

Ответ 9

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

for i in mylist:
    result += [i, mylist.pop()]

Примечание:

Остерегайтесь: Как сказал @Tadhg McDonald-Jensen (см. комментарий ниже) он уничтожит половину исходного объекта списка.

Ответ 10

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

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)]

Ответ 11

Не совсем элегантный, но это неуклюжий однострочный:

a = range(10)
[val for pair in zip(a[:len(a)//2],a[-1:(len(a)//2-1):-1]) for val in pair]

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

Ответ 12

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

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)))]

Ответ 13

Я бы сделал что-то вроде этого

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

Ответ 14

Один из способов сделать это для списков четного размера (вдохновленный этим сообщением):

a = range(10)

b = [val for pair in zip(a[:5], a[5:][::-1]) for val in pair]

Ответ 15

Вы можете разбить список на две части относительно середины, перевернуть вторую половину и застегнуть два раздела, например:

a = [0,1,2,3,4,5,6,7,8,9]
mid = len(a)//2
l = []
for x, y in zip(a[:mid], a[:mid-1:-1]):
    l.append(x)
    l.append(y)
# if the length is odd
if len(a) % 2 == 1:
    l.append(a[mid])
print(l)

Вывод:

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