Iteracja co dwa elementy na liście


Odpowiedzi:


231

Potrzebujesz implementacji pairwise()(lub grouped()).

W przypadku Python 2:

from itertools import izip

def pairwise(iterable):
    "s -> (s0, s1), (s2, s3), (s4, s5), ..."
    a = iter(iterable)
    return izip(a, a)

for x, y in pairwise(l):
   print "%d + %d = %d" % (x, y, x + y)

Lub bardziej ogólnie:

from itertools import izip

def grouped(iterable, n):
    "s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), (s2n,s2n+1,s2n+2,...s3n-1), ..."
    return izip(*[iter(iterable)]*n)

for x, y in grouped(l, 2):
   print "%d + %d = %d" % (x, y, x + y)

W Pythonie 3 możesz zastąpić izipgo wbudowaną zip()funkcją i upuścićimport .

Wszystko kredytowej Martineau na jego odpowiedź na moje pytanie , znalazłem to być bardzo skuteczna, ponieważ tylko iteracje raz na listę i nie tworzy żadnych zbędnych list w procesie.

Uwaga : nie należy tego mylić zpairwise przepisem we własnej itertoolsdokumentacji Pythona , która daje wynik s -> (s0, s1), (s1, s2), (s2, s3), ..., jak wskazał @lazyr w komentarzach.

Mały dodatek dla tych, którzy chcieliby sprawdzić typ za pomocą mypy w Pythonie 3:

from typing import Iterable, Tuple, TypeVar

T = TypeVar("T")

def grouped(iterable: Iterable[T], n=2) -> Iterable[Tuple[T, ...]]:
    """s -> (s0,s1,s2,...sn-1), (sn,sn+1,sn+2,...s2n-1), ..."""
    return zip(*[iter(iterable)] * n)

16
Nie należy mylić z funkcją parowania sugerowaną w sekcji receptur itertools , która dajes -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Robi to inaczej. Twoja wersja daje tylko połowę liczby par w porównaniu do itertoolsfunkcji receptury o tej samej nazwie. Oczywiście twoje jest szybsze ...
Sven Marnach

Co? Twoja funkcja i funkcja, o której mówiłem, robią różne rzeczy i taki był sens mojego komentarza.
Lauritz V. Thaulow

5
BĄDŹ OSTROŻNY! Korzystanie z tych funkcji stwarza ryzyko, że nie będziesz iterował ostatnich elementów iteracji. Przykład: lista (pogrupowane ([1,2,3], 2)) >>> [(1, 2)] .. kiedy można się spodziewać [(1,2), (3,)]
egafni

4
@ Erik49: W przypadku określonym w pytaniu nie ma sensu mieć krotki „niekompletnej”. Jeśli chcesz dołączyć niepełną krotkę, możesz użyć izip_longest()zamiast izip(). Np .: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Mam nadzieję że to pomoże.
Johnsyweb

191

Cóż, potrzebujesz krotkę 2 elementów, więc

data = [1,2,3,4,5,6]
for i,k in zip(data[0::2], data[1::2]):
    print str(i), '+', str(k), '=', str(i+k)

Gdzie:

  • data[0::2] oznacza tworzenie podzbioru kolekcji elementów, które (index % 2 == 0)
  • zip(x,y) tworzy kolekcję krotek z kolekcji xiy tych samych elementów indeksu.

8
Można to również rozszerzyć, jeśli wymagane są więcej niż dwa elementy. Na przykładfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
lifebalance

19
O wiele czystsze niż pobieranie importu i definiowanie funkcji!
kmarsh

7
@kmarsh: Ale działa to tylko na sekwencjach, funkcja działa na każdym iterowalnym; i to wykorzystuje dodatkowe miejsce O (N), funkcja nie; z drugiej strony jest to generalnie szybsze. Istnieją dobre powody, aby wybrać jedno lub drugie; banie się importnie jest jednym z nich.
abarnert

77
>>> l = [1,2,3,4,5,6]

>>> zip(l,l[1:])
[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

>>> zip(l,l[1:])[::2]
[(1, 2), (3, 4), (5, 6)]

>>> [a+b for a,b in zip(l,l[1:])[::2]]
[3, 7, 11]

>>> ["%d + %d = %d" % (a,b,a+b) for a,b in zip(l,l[1:])[::2]]
['1 + 2 = 3', '3 + 4 = 7', '5 + 6 = 11']

1
To nie działa na Python-3.6.0, ale nadal działa na Python-2.7.10
Hamid

6
@HamidRohani zipzwraca zipobiekt w Pythonie 3, który nie podlega indeksowi. Najpierw należy go przekonwertować na sekwencję ( list, tupleitd.), Ale „niedziałanie” jest trochę rozciągnięte.
vaultah

58

Proste rozwiązanie.

l = [1, 2, 3, 4, 5, 6]

dla i w zakresie (0, len (l), 2):
    print str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
co jeśli twoja lista nie jest równa, a chcesz po prostu pokazać ostatnią cyfrę taką, jaka jest?
Hans de Jong

@HansdeJong cię nie dostał. Proszę wyjaśnić trochę więcej.
taskinoor

2
Dzięki. Już wymyśliłem, jak to zrobić. Problem polegał na tym, że jeśli masz listę, która nie zawiera nawet liczby, otrzyma błąd indeksu. Rozwiązano to za pomocą: z wyjątkiem:
Hansa de Jonga

Lub ((l[i], l[i+1])for i in range(0, len(l), 2))dla generatora, można go łatwo modyfikować dla dłuższych krotek.
Basel Shishani,

44

Chociaż wszystkie użyte odpowiedzi zipsą poprawne, uważam, że samodzielne wdrożenie funkcji prowadzi do bardziej czytelnego kodu:

def pairwise(it):
    it = iter(it)
    while True:
        try:
            yield next(it), next(it)
        except StopIteration:
            # no more elements in the iterator
            return

W it = iter(it)części zapewnia, że itw rzeczywistości jest iterator, a nie tylko iterable. Jeśli itjest już iteratorem, linia ta nie ma op-op.

Stosowanie:

for a, b in pairwise([0, 1, 2, 3, 4, 5]):
    print(a + b)

2
To rozwiązanie pozwala na uogólnienie na rozmiar krotek> 2
guilloptero

1
To rozwiązanie działa również, jeśli itjest tylko iteratorem, a nie iteracją. Inne rozwiązania wydają się polegać na możliwości utworzenia dwóch niezależnych iteratorów dla sekwencji.
skyking

Znalazłem to podejście na stackoverflow.com/a/16815056/2480481, zanim zobaczyłem tę odpowiedź. Jest czystszy, łatwiejszy niż obsługa zip ().
m3nda

2
Podoba mi się, że pozwala uniknąć potrojenia wykorzystania pamięci jako przyjętej odpowiedzi.
Kentzo

Nie działa to dobrze z forpętlami w Pythonie 3.5+ ze względu na PEP 479 , który zastępuje wszelkie StopIterationpodniesione w generatorze przez RuntimeError.
sidney,

28

Mam nadzieję, że będzie to jeszcze bardziej elegancki sposób.

a = [1,2,3,4,5,6]
zip(a[::2], a[1::2])

[(1, 2), (3, 4), (5, 6)]

18

Jeśli jesteś zainteresowany wydajnością, zrobiłem mały test porównawczy (używając mojej biblioteki simple_benchmark) w celu porównania wydajności rozwiązań i załączyłem funkcję z jednego z moich pakietów:iteration_utilities.grouper

from iteration_utilities import grouper
import matplotlib as mpl
from simple_benchmark import BenchmarkBuilder

bench = BenchmarkBuilder()

@bench.add_function()
def Johnsyweb(l):
    def pairwise(iterable):
        "s -> (s0, s1), (s2, s3), (s4, s5), ..."
        a = iter(iterable)
        return zip(a, a)

    for x, y in pairwise(l):
        pass

@bench.add_function()
def Margus(data):
    for i, k in zip(data[0::2], data[1::2]):
        pass

@bench.add_function()
def pyanon(l):
    list(zip(l,l[1:]))[::2]

@bench.add_function()
def taskinoor(l):
    for i in range(0, len(l), 2):
        l[i], l[i+1]

@bench.add_function()
def mic_e(it):
    def pairwise(it):
        it = iter(it)
        while True:
            try:
                yield next(it), next(it)
            except StopIteration:
                return

    for a, b in pairwise(it):
        pass

@bench.add_function()
def MSeifert(it):
    for item1, item2 in grouper(it, 2):
        pass

bench.use_random_lists_as_arguments(sizes=[2**i for i in range(1, 20)])
benchmark_result = bench.run()
mpl.rcParams['figure.figsize'] = (8, 10)
benchmark_result.plot_both(relative_to=MSeifert)

wprowadź opis zdjęcia tutaj

Więc jeśli chcesz najszybszego rozwiązania bez zewnętrznych zależności, prawdopodobnie powinieneś po prostu zastosować podejście podane przez Johnysweba (w momencie pisania jest to najbardziej pozytywna i akceptowana odpowiedź).

Jeśli nie przeszkadza ci dodatkowa zależność, to grouperod iteration_utilitiesbędzie prawdopodobnie nieco szybsze.

Dodatkowe przemyślenia

Niektóre podejścia mają pewne ograniczenia, które nie zostały tutaj omówione.

Na przykład kilka rozwiązań działa tylko dla sekwencji (tj. List, ciągów itp.), Na przykład rozwiązania Margus / pyanon / taskinoor, które korzystają z indeksowania, podczas gdy inne rozwiązania działają na dowolnej iterowalnej (to znaczy sekwencje i generatorach, iteratorach), takim jak Johnysweb / mic_e / my solutions.

Następnie Johnysweb dostarczył również rozwiązanie, które działa dla rozmiarów innych niż 2, podczas gdy inne odpowiedzi nie (ok iteration_utilities.grouper pozwala również ustawić liczbę elementów na „grupę”).

Następnie pojawia się pytanie, co powinno się stać, jeśli na liście znajduje się nieparzysta liczba elementów. Czy pozostały element należy odrzucić? Czy listę należy uzupełnić, aby była równa? Czy pozostały element powinien zostać zwrócony jako pojedynczy? Druga odpowiedź nie odnosi się bezpośrednio do tego punktu, jednak jeśli niczego nie przeoczyłem, wszyscy stosują podejście, zgodnie z którym pozostały element powinien zostać odrzucony (z wyjątkiem odpowiedzi Taskinoors - to faktycznie spowoduje wyjątek).

Dzięki grouperniemu możesz zdecydować, co chcesz zrobić:

>>> from iteration_utilities import grouper

>>> list(grouper([1, 2, 3], 2))  # as single
[(1, 2), (3,)]

>>> list(grouper([1, 2, 3], 2, truncate=True))  # ignored
[(1, 2)]

>>> list(grouper([1, 2, 3], 2, fillvalue=None))  # padded
[(1, 2), (3, None)]

12

Użyj razem poleceń zipi iter:

Uważam, że to rozwiązanie iterjest dość eleganckie:

it = iter(l)
list(zip(it, it))
# [(1, 2), (3, 4), (5, 6)]

Który znalazłem w dokumentacji zip Python 3 .

it = iter(l)
print(*(f'{u} + {v} = {u+v}' for u, v in zip(it, it)), sep='\n')

# 1 + 2 = 3
# 3 + 4 = 7
# 5 + 6 = 11

Aby uogólnić na Nelementy jednocześnie:

N = 2
list(zip(*([iter(l)] * N)))
# [(1, 2), (3, 4), (5, 6)]

10
for (i, k) in zip(l[::2], l[1::2]):
    print i, "+", k, "=", i+k

zip(*iterable) zwraca krotkę z kolejnym elementem każdej iterowalnej.

l[::2] zwraca pierwszy, trzeci, piąty itd. element listy: pierwszy dwukropek wskazuje, że wycinek zaczyna się na początku, ponieważ nie ma za nim liczby, drugi dwukropek jest potrzebny tylko wtedy, gdy chcesz „kroku w wycinku” „(w tym przypadku 2).

l[1::2]robi to samo, ale zaczyna się w drugim elemencie list, więc zwraca drugi, czwarty, szósty itd. element oryginalnej listy.


4
Odpowiedź ta została już udzielona przez Margusa dwa lata temu. stackoverflow.com/questions/5389507/…
cababunga

1
1 do wyjaśnienia, jak [number::number]działa składnia. pomocne dla tych, którzy często nie używają Pythona
Alby

2

Z rozpakowaniem:

l = [1,2,3,4,5,6]
while l:
    i, k, *l = l
    print(str(i), '+', str(k), '=', str(i+k))

Łał! Dlaczego nie mogłem o tym myśleć :) Musisz po prostu poradzić sobie ze sprawą, w której nie ma absolutnej pary (nieparzyste wpisy)
Saurav Kumar

1

Dla każdego może to pomóc, oto rozwiązanie podobnego problemu, ale z nakładającymi się parami (zamiast wzajemnie wykluczających się par).

Z dokumentacji itertools Python :

from itertools import izip

def pairwise(iterable):
    "s -> (s0,s1), (s1,s2), (s2, s3), ..."
    a, b = tee(iterable)
    next(b, None)
    return izip(a, b)

Lub bardziej ogólnie:

from itertools import izip

def groupwise(iterable, n=2):
    "s -> (s0,s1,...,sn-1), (s1,s2,...,sn), (s2,s3,...,sn+1), ..."
    t = tee(iterable, n)
    for i in range(1, n):
        for j in range(0, i):
            next(t[i], None)
    return izip(*t)

1

możesz użyć pakietu more_itertools .

import more_itertools

lst = range(1, 7)
for i, j in more_itertools.chunked(lst, 2):
    print(f'{i} + {j} = {i+j}')

1

Muszę podzielić listę przez liczbę i tak ustawić.

l = [1,2,3,4,5,6]

def divideByN(data, n):
        return [data[i*n : (i+1)*n] for i in range(len(data)//n)]  

>>> print(divideByN(l,2))
[[1, 2], [3, 4], [5, 6]]

>>> print(divideByN(l,3))
[[1, 2, 3], [4, 5, 6]]

1

Można to zrobić na wiele sposobów. Na przykład:

lst = [1,2,3,4,5,6]
[(lst[i], lst[i+1]) for i,_ in enumerate(lst[:-1])]    
>>>[(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)]

[i for i in zip(*[iter(lst)]*2)]    
>>>[(1, 2), (3, 4), (5, 6)]

0

Pomyślałem, że to dobre miejsce, by podzielić się moim uogólnieniem na n> 2, które jest po prostu przesuwanym oknem nad iteracją:

def sliding_window(iterable, n):
    its = [ itertools.islice(iter, i, None) 
            for i, iter
            in enumerate(itertools.tee(iterable, n)) ]                               

    return itertools.izip(*its)

0

Tytuł tego pytania jest mylący, wydaje się, że szukasz kolejnych par, ale jeśli chcesz iterować zestaw wszystkich możliwych par, to zadziała:

for i,v in enumerate(items[:-1]):
        for u in items[i+1:]:

0

Używając pisania, aby zweryfikować dane za pomocą narzędzia do analizy statycznej mypy :

from typing import Iterator, Any, Iterable, TypeVar, Tuple

T_ = TypeVar('T_')
Pairs_Iter = Iterator[Tuple[T_, T_]]

def legs(iterable: Iterator[T_]) -> Pairs_Iter:
    begin = next(iterable)
    for end in iterable:
        yield begin, end
        begin = end

0

Uproszczone podejście:

[(a[i],a[i+1]) for i in range(0,len(a),2)]

jest to przydatne, jeśli twoja tablica jest a i chcesz iterować po niej parami. Aby wykonać iterację na trojaczkach lub więcej, wystarczy zmienić polecenie krokowe „zakres”, na przykład:

[(a[i],a[i+1],a[i+2]) for i in range(0,len(a),3)]

(musisz poradzić sobie z nadmiarem wartości, jeśli długość tablicy i stopień nie pasują)


-1

Tutaj możemy mieć alt_elemmetodę, która może pasować do twojej pętli for.

def alt_elem(list, index=2):
    for i, elem in enumerate(list, start=1):
        if not i % index:
           yield tuple(list[i-index:i])


a = range(10)
for index in [2, 3, 4]:
    print("With index: {0}".format(index))
    for i in alt_elem(a, index):
       print(i)

Wynik:

With index: 2
(0, 1)
(2, 3)
(4, 5)
(6, 7)
(8, 9)
With index: 3
(0, 1, 2)
(3, 4, 5)
(6, 7, 8)
With index: 4
(0, 1, 2, 3)
(4, 5, 6, 7)

Uwaga: powyższe rozwiązanie może nie być wydajne, biorąc pod uwagę operacje wykonywane w func.


-1
a_list = [1,2,3,4,5,6]
empty_list = [] 
for i in range(0,len(a_list),2):
   empty_list.append(a_list[i]+a_list[i+1])   
print(empty_list)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.