Jak mogę iterować listę obiektów, uzyskując dostęp do poprzednich, bieżących i następnych elementów? Jak ten kod C / C ++ w Pythonie?
foo = somevalue;
previous = next = 0;
for (i=1; i<objects.length(); i++) {
if (objects[i]==foo) {
previous = objects[i-1];
next = objects[i+1];
}
}
foo
wystąpi dokładnie jeden raz na liście? Jeśli wystąpi, pomnóż, niektóre podejścia tutaj zawiodą lub znajdą tylko pierwsze. A jeśli to się nigdy nie wydarzy, inne podejścia zawiodą lub rzucą wyjątki, takie jak ValueError. Przydałoby się kilka przypadków testowych.Odpowiedzi:
To powinno załatwić sprawę.
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]
Oto dokumentacja dotycząca
enumerate
funkcji.źródło
obj
inext_
będzie tym samym obiektem w ostatniej iteracji, co może mieć niezamierzone efekty uboczne.index
powinien uciekać od1 ... (l-1)
, a nie0 ... l
tak, jak tutaj, i nie ma potrzeby stosowania specjalnych klauzul if. Przy okazji, jest parametr,enumerate(..., start=1)
ale nie dlaend
. Więc naprawdę nie chcemy używaćenumerate()
.Dotychczasowe rozwiązania zajmowały się tylko listami, a większość z nich kopiuje listę. Z mojego doświadczenia wynika, że wiele razy nie jest to możliwe.
Nie zajmują się też tym, że na liście mogą znajdować się powtarzające się elementy.
Tytuł pytania brzmi „ Poprzednie i następne wartości wewnątrz pętli ”, ale jeśli większość odpowiedzi uruchomisz w pętli, w końcu ponownie przejdziesz przez całą listę dla każdego elementu, aby go znaleźć.
Więc właśnie stworzyłem funkcję, która. używając
itertools
modułu, dzieli i dzieli iterowalne elementy oraz generuje krotki z poprzednimi i następnymi elementami razem. Nie do końca to, co robi twój kod, ale warto przyjrzeć się temu, ponieważ prawdopodobnie może rozwiązać twój problem.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)
Następnie użyj go w pętli, a będziesz mieć w nim poprzednie i następne elementy:
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
Wyniki:
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
Będzie działać z każdą listą rozmiarów (ponieważ nie kopiuje listy) oraz z dowolnymi iterowalnymi (pliki, zestawy itp.). W ten sposób możesz po prostu iterować po sekwencji i mieć poprzednie i następne elementy dostępne w pętli. Nie ma potrzeby ponownego wyszukiwania pozycji w sekwencji.
Krótkie wyjaśnienie kodu:
tee
służy do wydajnego tworzenia 3 niezależnych iteratorów w sekwencji wejściowejchain
łączy dwie sekwencje w jedną; jest tutaj używany do dołączania sekwencji jednoelementowej[None]
doprevs
islice
służy do tworzenia sekwencji wszystkich elementów z wyjątkiem pierwszego, a następniechain
służy do dołączaniaNone
na jego końcu asome_iterable
które wyglądają następująco:prevs
:None, A, B, C, D, E
items
:A, B, C, D, E
nexts
:B, C, D, E, None
izip
służy do zamiany 3 sekwencji w jedną sekwencję trojaczków.Zauważ, że
izip
zatrzymuje się, gdy jakakolwiek sekwencja wejściowa zostanie wyczerpana, więc ostatni elementprevs
zostanie zignorowany, co jest poprawne - nie ma takiego elementu, który byłby jego ostatnim elementemprev
. Moglibyśmy spróbować oderwać ostatnie elementy od,prevs
aleizip
zachowanie sprawia, że jest to zbędneNależy również pamiętać, że
tee
,izip
,islice
ichain
pochodzą zitertools
modułu; operują na swoich sekwencjach wejściowych w locie (leniwie), co czyni je wydajnymi i nie wprowadza konieczności posiadania w pamięci całej sekwencji na raz.W
python 3
programie pokaże błąd podczas importowaniaizip
, możesz użyćzip
zamiastizip
. Nie ma potrzeby importuzip
, predefiniowane jest wpython 3
- źródłoźródło
izip
można go zastąpić wbudowanązip
funkcją ;-)Korzystając z funkcji list, zwróć 3 krotkę z bieżącymi, poprzednimi i następnymi elementami:
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)]
źródło
Nie wiem, jak to jeszcze nie wyszło, ponieważ wykorzystuje tylko wbudowane funkcje i można je łatwo rozszerzyć na inne przesunięcia:
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)
źródło
Oto wersja wykorzystująca generatory bez błędów granicznych:
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))
Zwróć uwagę, że zachowanie brzegowe polega na tym, że nigdy nie szukamy „foo” w pierwszym lub ostatnim elemencie, w przeciwieństwie do kodu. Ponownie, semantyka granic jest dziwna ... i trudno ją pojąć z twojego kodu :)
źródło
używanie wyrażeń warunkowych dla zwięzłości dla Pythona> = 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)
źródło
Dla każdego, kto szuka rozwiązania tego problemu, a także chce cyklicznie przełączać elementy, poniżej może zadziałać -
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)
źródło
objects[i-1], objects[i], objects[i+1]
? czy generator? Po prostu wydaje mi się to całkowicie obskurantystyczne. Również niepotrzebnie używa 3x pamięci, ponieważ PREV i NEXT wykonują kopie danych.i+1
podejście działające dla ostatniego elementu na liście? To następny element powinien być pierwszym. Wychodzę poza granice.Korzystanie z generatorów jest dość proste:
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 )
Zauważ, że testy, które zawierają wartość None i taką samą wartość jak wartość sygnału, nadal działają, ponieważ sprawdzanie wartości sygnału używa „is”, a sygnał jest wartością, której Python nie internuje. Jako sygnał można jednak użyć dowolnej wartości znacznika pojedynczego, co może w pewnych okolicznościach uprościć kod użytkownika.
źródło
if iB is signal
do porównywania obiektów pod kątem równości, chyba że signal = None, w takim przypadku po prostu napiszNone
już bezpośrednio . Nie używajiter
jako nazwy argumentu, ponieważ przesłania to wbudowaneiter()
. Jwnext
. W każdym razie podejście generatora może być po prostuyield prev, curr, next_
is
zamiast==
], to jest to dobrze znana pułapka. Oto kilka powodów, dla których: możesz uciec z tego w przypadku ciągów znaków, ponieważ polegasz na łańcuchach internowania cPythona, ale nawetv1 = 'monkey'; v2 = 'mon'; v3 = 'key
wtedyv1 is (v2 + v3)
dajeFalse
. A jeśli twój kod kiedykolwiek przełączy się na używanie obiektów zamiast int / strings, użycieis
zakończy się niepowodzeniem. Więc ogólnie powinieneś używać==
do porównywania równości.Dwa proste rozwiązania:
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
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
źródło
Możesz po prostu użyć
index
listy, aby znaleźć, gdziesomevalue
jest, a następnie w razie potrzeby pobrać poprzednie i następne:def find_prev_next(elem, elements): previous, next = None, None index = elements.index(elem) if index > 0: previous = elements[index -1] if index < (len(elements)-1): next = elements[index +1] return previous, next foo = 'three' list = ['one','two','three', 'four', 'five'] previous, next = find_prev_next(foo, list) print previous # should print 'two' print next # should print 'four'
źródło
AFAIK powinno to być dość szybkie, ale nie testowałem tego:
def iterate_prv_nxt(my_list): prv, cur, nxt = None, iter(my_list), iter(my_list) next(nxt, None) while True: try: if prv: yield next(prv), next(cur), next(nxt, None) else: yield None, next(cur), next(nxt, None) prv = iter(my_list) except StopIteration: break
Przykładowe użycie:
>>> my_list = ['a', 'b', 'c'] >>> for prv, cur, nxt in iterate_prv_nxt(my_list): ... print prv, cur, nxt ... None a b a b c b c None
źródło
Myślę, że to działa i nie jest skomplikowane
array= [1,5,6,6,3,2] for i in range(0,len(array)): Current = array[i] Next = array[i+1] Prev = array[i-1]
źródło
Rozwiązanie w stylu bardzo C / C ++:
foo = 5 objectsList = [3, 6, 5, 9, 10] prev = nex = 0 currentIndex = 0 indexHigher = len(objectsList)-1 #control the higher limit of list found = False prevFound = False nexFound = False #main logic: for currentValue in objectsList: #getting each value of list if currentValue == foo: found = True if currentIndex > 0: #check if target value is in the first position prevFound = True prev = objectsList[currentIndex-1] if currentIndex < indexHigher: #check if target value is in the last position nexFound = True nex = objectsList[currentIndex+1] break #I am considering that target value only exist 1 time in the list currentIndex+=1 if found: print("Value %s found" % foo) if prevFound: print("Previous Value: ", prev) else: print("Previous Value: Target value is in the first position of list.") if nexFound: print("Next Value: ", nex) else: print("Next Value: Target value is in the last position of list.") else: print("Target value does not exist in the list.")
źródło
Pythonic i elegancki sposób:
objects = [1, 2, 3, 4, 5] value = 3 if value in objects: index = objects.index(value) previous_value = objects[index-1] next_value = objects[index+1] if index + 1 < len(objects) else None
źródło
value
jest na końcu. Zwraca także ostatni element tak,previous_value
jakbyvalue
był pierwszym.previous_value
zwróci ostatni element z listy inext_value
podniesieIndexError
błądvalue
może wystąpić więcej niż raz wobjects
, ale użycie.index()
spowoduje odnalezienie pierwszego wystąpienia (lub ValueError, jeśli nie wystąpi).