101 votes

Boucle qui accède également aux valeurs précédentes et suivantes

Comment puis-je itérer sur une liste d'objets, en accédant aux éléments précédents, actuels et suivants ? Comme ce code C/C++, en Python ?

foo = somevalue;
previous = next = 0;

for (i=1; i<objects.length(); i++) {
    if (objects[i]==foo) {
        previous = objects[i-1];
        next = objects[i+1];
    }
}

164voto

nosklo Points 75862

Jusqu'à présent, les solutions ne concernent que les listes, et la plupart d'entre elles copient la liste. D'après mon expérience, ce n'est souvent pas possible.

En outre, elles ne tiennent pas compte du fait que la liste peut contenir des éléments répétés.

Le titre de votre question dit " Valeurs précédentes et suivantes dans une boucle "Mais si vous exécutez la plupart des réponses ici à l'intérieur d'une boucle, vous finirez par itérer sur la liste entière à nouveau sur chaque élément pour le trouver.

Donc, je viens de créer une fonction qui. en utilisant la itertools divise et découpe l'itérable, et génère des tuples avec les éléments précédents et suivants ensemble. Ce n'est pas exactement ce que fait votre code, mais cela vaut la peine d'y jeter un coup d'œil, car cela peut probablement résoudre votre problème.

from itertools import tee, islice, chain, izip

def previous_and_next(some_iterable):
    prevs, items, nexts = tee(some_iterable, 3)
    prevs = chain([None], prevs)
    nexts = chain(islice(nexts, 1, None), [None])
    return izip(prevs, items, nexts)

Utilisez-le ensuite dans une boucle, et vous aurez des éléments précédents et suivants :

mylist = ['banana', 'orange', 'apple', 'kiwi', 'tomato']

for previous, item, nxt in previous_and_next(mylist):
    print "Item is now", item, "next is", nxt, "previous is", previous

Les résultats :

Item is now banana next is orange previous is None
Item is now orange next is apple previous is banana
Item is now apple next is kiwi previous is orange
Item is now kiwi next is tomato previous is apple
Item is now tomato next is None previous is kiwi

Il fonctionnera avec une liste de n'importe quelle taille (parce qu'il ne copie pas la liste), et avec n'importe quel itérable (fichiers, ensembles, etc). De cette façon, vous pouvez simplement itérer sur la séquence, et avoir les éléments précédents et suivants disponibles dans la boucle. Il n'est pas nécessaire de rechercher à nouveau l'élément dans la séquence.

Une brève explication du code :

  • tee est utilisé pour créer efficacement 3 itérateurs indépendants sur la séquence d'entrée
  • chain relie deux séquences en une seule ; il est utilisé ici pour ajouter une séquence à un seul élément. [None] a prevs
  • islice est utilisé pour faire une séquence de tous les éléments sauf le premier, alors chain est utilisé pour ajouter un None jusqu'à son terme
  • Il y a maintenant 3 séquences indépendantes basées sur some_iterable qui ressemblent :
    • prevs : None, A, B, C, D, E
    • items : A, B, C, D, E
    • nexts : B, C, D, E, None
  • enfin izip est utilisé pour transformer 3 séquences en une séquence de triplets.

Notez que izip s'arrête lorsque toute séquence d'entrée est épuisée, donc le dernier élément de prevs sera ignoré, ce qui est correct - il n'y a pas de tel élément que le dernier élément serait son prev . Nous pourrions essayer de retirer les derniers éléments de prevs mais izip Le comportement de l'entreprise rend cela superflu

Notez également que tee , izip , islice y chain proviennent de la itertools Ils opèrent sur leurs séquences d'entrée à la volée (paresseusement), ce qui les rend efficaces et n'introduit pas la nécessité d'avoir la séquence entière en mémoire à tout moment.

Sur python 3 il y aura une erreur lors de l'importation izip ,vous pouvez utiliser zip au lieu de izip . Il n'est pas nécessaire d'importer zip il est prédéfini dans python 3 - fuente

121voto

Hank Gay Points 36173

Cela devrait faire l'affaire.

foo = somevalue
previous = next_ = None
l = len(objects)
for index, obj in enumerate(objects):
    if obj == foo:
        if index > 0:
            previous = objects[index - 1]
        if index < (l - 1):
            next_ = objects[index + 1]

Voici la documentation sur le enumerate fonction.

7voto

RYS Points 259

En utilisant une compréhension de liste, renvoyer un 3-tuple avec les éléments courant, précédent et suivant :

three_tuple = [(current, 
                my_list[idx - 1] if idx >= 1 else None, 
                my_list[idx + 1] if idx < len(my_list) - 1 else None) for idx, current in enumerate(my_list)]

5voto

Eric Czech Points 411

Je ne sais pas comment cela n'a pas encore été fait puisqu'il n'utilise que des fonctions intégrées et est facilement extensible à d'autres décalages :

values = [1, 2, 3, 4]
offsets = [None] + values[:-1], values, values[1:] + [None]
for value in list(zip(*offsets)):
    print(value) # (previous, current, next)

(None, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, None)

4voto

Boris Points 1440

Si vous voulez seulement itérer sur les éléments qui ont un élément suivant et un élément précédent (par exemple, vous voulez sauter le premier et le dernier élément) et votre entrée est une liste, vous pouvez zip l'entrée avec elle-même sans le premier élément et sans le deuxième élément :

words = "one two three four five".split()

for prev, current, nxt in zip(words, words[1:], words[2:]):
    print(prev, current, nxt)

Sortie :

one two three
two three four
three four five

Si vous ne voulez pas sauter le premier et le dernier élément, et que vous voulez prev à régler sur None lorsque vous êtes sur le premier élément (et nxt ser None pour le dernier élément), remplissez votre liste avec ces valeurs en premier :

words = "one two three four five".split()

padded_words = [None, *words, None]

for prev, current, nxt in zip(padded_words, padded_words[1:], padded_words[2:]):
    print(prev, current, nxt)

Sortie :

None one two
one two three
two three four
three four five
four five None

Vous pouvez utiliser tout ce que vous voulez. Si vous voulez que votre liste soit "enveloppée" (comme dans le cas de la fonction prev du premier élément est le dernier élément et le nxt du dernier élément est le premier élément), remplissez votre entrée avec ces éléments au lieu de None :

# avoid IndexError if words is an empty list
padded_words = [words[-1], *words, words[0]] if words else []

Sortie :

five one two
one two three
two three four
three four five
four five one

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