29 votes

Cycle d'une liste à partir de côtés alternés

Étant donné une liste

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

comment puis-je obtenir

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

C'est-à-dire produire une nouvelle liste dans laquelle chaque élément successif est alternativement pris dans les deux côtés de la liste originale ?

1 votes

Pourquoi pas deque ?! l1=list(range(10)); d1=deque(l1); [d1.pop() if i%2 else d1.popleft() for i,_ in enumerate(l1) if d1]

52voto

Norman Points 1487
>>> [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]

Explication :
Ce code prend les numéros à partir du début ( a[i//2] ) et de la fin ( a[-i//2] ) de a en alternance ( if i%2 else ). Un total de len(a) les numéros sont choisis, ce qui ne produit pas d'effets néfastes même si len(a) est impair.
[-i//2 for i in range(len(a))] donne 0, -1, -1, -2, -2, -3, -3, -4, -4, -5 ,
[ i//2 for i in range(len(a))] donne 0, 0, 1, 1, 2, 2, 3, 3, 4, 4 ,
y i%2 alterne entre False y True ,
donc les indices que nous extrayons de a sont : 0, -1, 1, -2, 2, -3, 3, -4, 4, -5 .

Mon évaluation du caractère pythonique :
Ce qui est bien avec cette phrase, c'est qu'elle est courte et qu'elle présente une symétrie ( +i//2 y -i//2 ).
Le problème, cependant, c'est que cette symétrie est trompeuse :
On pourrait penser que -i//2 étaient les mêmes que i//2 avec le signe inversé. Mais en Python, la division d'un nombre entier renvoie la valeur plancher. du résultat au lieu de tronquer vers zéro. Ainsi, -1//2 == -1 .
De plus, je trouve l'accès aux éléments d'une liste par index moins pythique que l'itération.

0 votes

Vous détenez actuellement le record de la seule doublure qui n'a pas besoin d'une barre de défilement et qui fonctionne avec des listes de longueur impaire, puisque le PO a a déclaré qu'il aimait les phrases à une ligne Je serais enclin à dire que ceci est la meilleure réponse (+1 btw)

2 votes

C'est également l'une des rares réponses qui ne pose pas de problème avec les listes de taille impaire. Bien joué !

0 votes

Il n'y a pas d'élégance, car personne ne peut comprendre immédiatement ce qu'il fait en regardant ce code.

29voto

cycle entre l'obtention d'éléments de l'avant iter et le reversed un. Assurez-vous juste de vous arrêter à len(a) avec 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]

Cela peut facilement être mis en une seule ligne, mais cela devient alors beaucoup plus difficile à lire :

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

Le fait de le mettre sur une seule ligne vous empêcherait également d'utiliser l'autre moitié des itérateurs si vous le souhaitiez :

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

1 votes

En fait, je préfère les deux lignes que vous avez faites à partir de l'idée de @canaaerus à la réponse acceptée. Voir i//2 Je dois encore réfléchir à la raison pour laquelle l'indice doit être divisé par deux, mais avec [next(iters[n%2]) for n in a] Je comprends immédiatement . À mon avis, cette phrase est l'interprétation la plus fidèle de l'expression "Prenez alternativement les numéros du début et de la fin de la liste". Je pense qu'il mérite une place plus importante qu'à la fin de votre réponse.

0 votes

@norman j'ai depuis édité mon post pour utiliser itertools.cycle que j'aime encore plus que ma version précédente.

0 votes

Waouh, joli :-) C'est est encore mieux. Y a-t-il des limites à la lisibilité du code Python ?

8voto

Akshat Mahajan Points 5438

Un très beau one-liner en Python 2.7 :

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

D'abord, vous zippez la liste avec son inverse, prenez moitié cette liste, additionner les tuples pour former un seul tuple, et puis convertir en liste.

Dans Python 3, zip renvoie un générateur, vous devez donc utiliser islice de itertools :

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

Modifier : Il semble que cela ne fonctionne parfaitement que pour les longueurs de liste paires - les longueurs de liste impaires omettent l'élément du milieu :( Une petite correction pour int(len(a)/2) a int(len(a)/2) + 1 vous donnera un duplicata de la valeur moyenne, alors soyez avertis.

0 votes

TypeError : l'objet 'zip' n'est pas subscriptable

0 votes

@canaaerus Cela fonctionne dans Python 2.7. Laissez-moi voir ce que je peux faire dans Python 3.

0 votes

@canaaerus zip renvoie un générateur en 3.5, et une liste en 2.7. Utilisez itertools.islice .

6voto

Zach Gates Points 3090

Vous pouvez juste pop d'avant en arrière :

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

Note : Ceci détruit la liste originale, a .

1 votes

La liste originale était a et cela détruit la liste originale.

0 votes

@TadhgMcDonald-Jensen : Le PO n'a pas demandé une méthode non-destructive.

1 votes

Non mais c'est quelque chose que vous voulez vraiment mentionner dans votre réponse.

6voto

skrx Points 13884

Utilisez le bon toolz .

from toolz import interleave, take

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

Tout d'abord, j'ai essayé quelque chose de similaire à la solution de Raymond Hettinger avec itertools (Python 3).

from itertools import chain, islice

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

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X