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

4voto

moshez Points 9516

Voici une version utilisant des générateurs sans erreurs de limites :

def trios(iterable):
    it = iter(iterable)
    try:
        prev, current = next(it), next(it)
    except StopIteration:
        return
    for next in it:
        yield prev, current, next
        prev, current = current, next

def find_prev_next(objects, foo):
    prev, next = 0, 0
    for temp_prev, current, temp_next in trios(objects):
        if current == foo:
            prev, next = temp_prev, temp_next
    return prev, next

print(find_prev_next(range(10), 1))
print(find_prev_next(range(10), 0))
print(find_prev_next(range(10), 10))
print(find_prev_next(range(0), 10))
print(find_prev_next(range(1), 10))
print(find_prev_next(range(2), 10))

Veuillez noter que le comportement de la frontière est que nous ne cherchons jamais "foo" dans le premier ou le dernier élément, contrairement à votre code. Encore une fois, la sémantique des limites est étrange... et difficile à comprendre à partir de votre code :)

2voto

makapuf Points 744

Utilisation d'expressions conditionnelles pour plus de concision pour python >= 2.5

def prenext(l,v) : 
   i=l.index(v)
   return l[i-1] if i>0 else None,l[i+1] if i<len(l)-1 else None

# example
x=range(10)
prenext(x,3)
>>> (2,4)
prenext(x,0)
>>> (None,2)
prenext(x,9)
>>> (8,None)

2voto

skr47ch Points 29

Pour tous ceux qui cherchent une solution à ce problème tout en voulant faire du vélo avec les éléments, la solution ci-dessous pourrait fonctionner.

from collections import deque  

foo = ['A', 'B', 'C', 'D']

def prev_and_next(input_list):
    CURRENT = input_list
    PREV = deque(input_list)
    PREV.rotate(-1)
    PREV = list(PREV)
    NEXT = deque(input_list)
    NEXT.rotate(1)
    NEXT = list(NEXT)
    return zip(PREV, CURRENT, NEXT)

for previous_, current_, next_ in prev_and_next(foo):
    print(previous_, current_, next)

2voto

Serge Tochilov Points 111

Deux solutions simples :

  1. Si des variables pour les valeurs précédentes et suivantes doivent être définies :

    alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

    prev = alist[0] curr = alist[1]

    for nxt in alist[2:]: print(f'prev: {prev}, curr: {curr}, next: {nxt}') prev = curr curr = nxt

    Output[1]: prev: Zero, curr: One, next: Two prev: One, curr: Two, next: Three prev: Two, curr: Three, next: Four prev: Three, curr: Four, next: Five

  2. Si toutes les valeurs de la liste doivent être parcourues par la variable de valeur courante :

    alist = ['Zero', 'One', 'Two', 'Three', 'Four', 'Five']

    prev = None curr = alist[0]

    for nxt in alist[1:] + [None]: print(f'prev: {prev}, curr: {curr}, next: {nxt}') prev = curr curr = nxt

    Output[2]: prev: None, curr: Zero, next: One prev: Zero, curr: One, next: Two prev: One, curr: Two, next: Three prev: Two, curr: Three, next: Four prev: Three, curr: Four, next: Five prev: Four, curr: Five, next: None

1voto

Victoria Points 63

L'utilisation de générateurs est assez simple :

signal = ['→Signal value←']
def pniter( iter, signal=signal ):
    iA = iB = signal
    for iC in iter:
        if iB is signal:
            iB = iC
            continue
        else:
            yield iA, iB, iC
        iA = iB
        iB = iC
    iC = signal
    yield iA, iB, iC

if __name__ == '__main__':
    print('test 1:')
    for a, b, c in pniter( range( 10 )):
        print( a, b, c )
    print('\ntest 2:')
    for a, b, c in pniter([ 20, 30, 40, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 3:')
    cam = { 1: 30, 2: 40, 10: 9, -5: 36 }
    for a, b, c in pniter( cam ):
        print( a, b, c )
    for a, b, c in pniter( cam ):
        print( a, a if a is signal else cam[ a ], b, b if b is signal else cam[ b ], c, c if c is signal else cam[ c ])
    print('\ntest 4:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ]):
        print( a, b, c )
    print('\ntest 5:')
    for a, b, c in pniter([ 20, 30, None, 50, 60, 70, 80 ], ['sig']):
        print( a, b, c )
    print('\ntest 6:')
    for a, b, c in pniter([ 20, ['→Signal value←'], None, '→Signal value←', 60, 70, 80 ], signal ):
        print( a, b, c )

Notez que les tests qui incluent None et la même valeur que la valeur du signal fonctionnent toujours, car la vérification de la valeur du signal utilise "is" et le signal est une valeur que Python n'internise pas. Cependant, toute valeur de marqueur singleton peut être utilisée comme signal, ce qui peut simplifier le code utilisateur dans certaines circonstances.

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