Listedeki her iki öğeyi yineleme


Yanıtlar:


231

Bir pairwise()(veya grouped()) uygulamaya ihtiyacınız var .

Python 2 için:

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)

Veya daha genel olarak:

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)

Python 3'te, izipyerleşikzip() işlevle ve simgesini bırakabilirsiniz import.

Tüm kredi Martineau için onun cevabını için sorumu , ben sadece liste üzerinde bir kez dolaşır ve süreç içinde gereksiz listeleri yaratmaz olarak çok verimli olması için bu bulduk.

NB : Bu ile karıştırılmamalıdır pairwisetarifi Python'un kendi içinde itertoolsbelgeler , verimleri s -> (s0, s1), (s1, s2), (s2, s3), ...ile sivri out gibi, @lazyr , yorumlarda .

Python 3'te mypy ile tip kontrolü yapmak isteyenler için küçük bir ek :

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
Önerilen ikili fonksiyonu ile karıştırılmamalıdır itertools hangi verimleri tarifleri bölümünde,s -> (s0,s1), (s1,s2), (s2, s3), ...
Lauritz V. Thaulow

1
Farklı bir şey yapar. Sürümünüz itertools, aynı ada sahip tarif işlevine kıyasla çift sayısının yalnızca yarısını verir . Tabii seninki daha hızlı ...
Sven Marnach

Ha? Sizin işleviniz ve bahsettiğim işlev farklı şeyler yapıyor ve bu benim yorumumun konusu oldu.
Lauritz V. Thaulow

5
DİKKATLİ OL! Bu işlevleri kullanmak, bir yinelenebilir öğenin son öğelerini yineleme riskini ortadan kaldırır. Örnek: liste (gruplandırılmış ([1,2,3], 2)) >>> [(1, 2)] .. beklediğiniz zaman [(1,2), (3,)]
egafni

4
@ Erik49: Soruda belirtilen durumda, 'eksik' bir demet olması mantıklı olmaz. Eksik bir demet eklemek istiyorsanız, izip_longest()yerine kullanabilirsiniz izip(). Örneğin: list(izip_longest(*[iter([1, 2, 3])]*2, fillvalue=0))-> [(1, 2), (3, 0)]. Bu yardımcı olur umarım.
Johnsyweb

191

Peki 2 öğeden oluşan bir gruba ihtiyacınız var, yani

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)

Nerede:

  • data[0::2] şu öğelerin alt küme koleksiyonunu oluşturmak anlamına gelir: (index % 2 == 0)
  • zip(x,y) x ve y koleksiyonlarından aynı indeks elemanlarından bir grup koleksiyonu oluşturur.

8
İkiden fazla eleman gerektiğinde bu genişletilebilir. Örneğinfor i, j, k in zip(data[0::3], data[1::3], data[2::3]):
yaşam boyu

19
Bir içe aktarma yapmak ve bir işlevi tanımlamaktan çok daha temiz!
kmarsh

7
@kmarsh: Ama bu sadece diziler üzerinde çalışıyor, fonksiyon herhangi bir yinelenebilir şekilde çalışıyor; ve bu O (N) fazladan alan kullanır, fonksiyon kullanmaz; Öte yandan, bu genellikle daha hızlıdır. Birini veya diğerini seçmek için iyi nedenler var; korkmak importbunlardan biri değil.
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
Bu Python-3.6.0 üzerinde çalışmıyor, ancak yine de Python-2.7.10 üzerinde çalışıyor
Hamid Rohani

6
@HamidRohani , Python 3'te abone olmayan zipbir zipnesne döndürür . İlk önce bir sekansa ( list, tuplevb.) Dönüştürülmesi gerekir , ancak "çalışmıyor" biraz esnektir.
vaultah

58

Basit bir çözüm.

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

(0, len (l), 2) aralığında i için:
    baskı str (l [i]), '+', str (l [i + 1]), '=', str (l [i] + l [i + 1])

1
ya listeniz düzgün değilse ve sadece son numarayı olduğu gibi göstermek istiyorsanız?
Hans de Jong

@HansdeJong seni anlamadı. Lütfen biraz daha açıklayın.
taskinoor

2
Teşekkürler. Bunu nasıl yapacağımı çoktan anladım. Sorun, içinde sayı bile olmayan bir listeniz varsa, bir dizin hatası alırdı. Bir deney ile çözüldü: hariç:
Hans de Jong

Veya ((l[i], l[i+1])for i in range(0, len(l), 2))bir jeneratör için, daha uzun tuples için kolayca değiştirilebilir.
Basel Shishani

44

Tüm cevaplar zipdoğru olsa da , işlevselliği kendiniz uygulamanın daha okunabilir koda götürdüğünü görüyorum:

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

it = iter(it)Bölüm olmasını sağlar itaslında bir yineleyici değil, sadece bir iterable olduğunu. Eğer itzaten Yineleyicinin, bu hat no-operasyon.

Kullanımı:

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

2
Bu çözüm> 2
guilloptero

1
Bu çözüm aynı zamanda itsadece bir yineleyici ve yinelenebilir değilse de çalışır . Diğer çözümler, dizi için iki bağımsız yineleyici oluşturma olasılığına güveniyor gibi görünüyor.
skyking

Bu yaklaşımı, bu yanıtı görmeden önce stackoverflow.com/a/16815056/2480481 adresinde buldum . Zip () ile uğraşmaktan daha temiz, daha kolaydır.
m3nda

2
Kabul edilen cevap olarak üç kat bellek kullanımını önlemeye izin veriyorum.
Kentzo

Bu, bir jeneratörde yükseltilmiş forolanları bir ile değiştiren PEP 479 nedeniyle Python 3.5+'daki döngülerle iyi çalışmaz . StopIterationRuntimeError
sidney

28

Umarım bunu yapmanın daha zarif bir yolu olacaktır.

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

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

18

Performansla ilgileniyorsanız simple_benchmark, çözümlerin performansını karşılaştırmak için küçük bir kıyaslama yaptım (kütüphanemi kullanarak ) ve paketlerimden birinden bir işlev ekledim: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)

resim açıklamasını buraya girin

Dış bağımlılıklar olmadan en hızlı çözümü istiyorsanız, muhtemelen sadece Johnysweb tarafından verilen yaklaşımı kullanmalısınız (yazarken en doğru ve kabul edilen cevaptır).

Sonra ek bağımlılık sakıncası yoksa groupergeleniteration_utilities muhtemelen biraz daha hızlı olacaktır.

Ek düşünceler

Bazı yaklaşımların burada tartışılmayan bazı kısıtlamaları vardır.

Örneğin, birkaç çözüm sadece sekanslar (listeler, dizeler vb.) İçin çalışır, örneğin diğer çözümler herhangi bir yinelenebilir (yani sekanslar ve sekanslar) üzerinde çalışırken indeksleme kullanan Margus / pyanon / taskinoor çözümleri Johnysweb gibi jeneratör, iterators) / mic_e / çözümlerim.

Daha sonra Johnysweb ayrıca 2'den farklı boyutlarda çalışan bir çözüm sunarken, diğer cevaplar işe yaramaz (tamam, aynı iteration_utilities.grouperzamanda eleman sayısının "grup" olarak ayarlanmasına da izin verir).

Listede tek sayıda eleman varsa ne olması gerektiği hakkında da bir soru var. Kalan eşya reddedilmeli mi? Liste, boyutunun eşit olması için doldurulmalı mıdır? Kalan ürün tek olarak iade edilmeli mi? Diğer cevap bu noktayı doğrudan ele almaz, ancak hiçbir şeyi göz ardı etmediysem, hepsi kalan öğenin reddedilmesi gerektiği yaklaşımını izler (taskinoors cevabı hariç - bu gerçekten bir istisna yaratacaktır).

İle grouperne yapmak istediğinize karar verebilirsiniz:

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

zipVe iterkomutlarını birlikte kullanın :

Bu çözümü iteroldukça zarif kullanarak buluyorum :

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

Hangi Python 3 zip belgelerinde buldum .

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

NBir defada elemanlara genelleme yapmak için :

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) her bir yinelenebilir öğenin bir sonraki elemanı olan bir demet döndürür.

l[::2] listenin 1., 3., 5. vb. öğelerini döndürür: ilk iki nokta, dilimin başında başladığını gösterir, çünkü arkasında sayı yok, ikinci iki nokta yalnızca dilimde bir 'adım istiyorsanız '(bu durumda 2).

l[1::2]aynı şeyi yapar ancak listenin ikinci öğesinde başlar, böylece orijinal listenin 2., 4., 6. vb. öğelerini döndürür .


4
Bu cevap zaten Margus tarafından iki yıl önce verildi. stackoverflow.com/questions/5389507/…
cababunga

1
1 [number::number]sözdiziminin nasıl çalıştığını açıklamak için . sık sık python kullanmayanlar için yararlı
Alby

2

Ambalajdan çıkarıldığında:

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

Vaov! Neden bunu düşünemedim :) Mutlak çiftin (tek girişler) olmadığı durumda davayı ele almanız gerekiyor
Saurav Kumar

1

Herkes için yardımcı olabilir, burada benzer bir soruna ancak üst üste gelen çiftlere (karşılıklı olarak ayrı çiftler yerine) bir çözüm var.

Python itertools belgelerinden :

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)

Veya daha genel olarak:

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

more_itertools paketini kullanabilirsiniz .

import more_itertools

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

1

Bir listeyi bir sayıya bölmem ve böyle sabitlemem gerekiyor.

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

Bunu yapmanın birçok yolu var. Örneğin:

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

Bunun, genelleştirilebilirliğimi n> 2 için paylaşmak için iyi bir yer olduğunu düşündüm.

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

Bu sorunun başlığı yanıltıcıdır, ardışık çiftler arıyor gibi görünüyorsunuz, ancak olası tüm çiftlerin setinin üzerinde çalışacağından daha fazlasını yapmak istiyorsanız:

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

0

Mypy statik analiz aracını kullanarak verileri doğrulayabilmeniz için yazmayı kullanma :

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

Basit bir yaklaşım:

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

diziniz a ise ve üzerinde çiftler halinde yineleme yapmak istiyorsanız bu yararlıdır. Üçüz veya daha fazlasında yineleme yapmak için sadece "aralık" adım komutunu değiştirin, örneğin:

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

(dizi uzunluğunuz ve adımınız uymuyorsa aşırı değerlerle uğraşmanız gerekir)


-1

Burada alt_elemfor döngüsü için uygun bir yöntem olabilir.

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)

Çıktı:

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)

Not: Yukarıdaki çözüm işlevde gerçekleştirilen işlemler düşünüldüğünde etkili olmayabilir.


-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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.