webentwicklung-frage-antwort-db.com.de

Iterieren Sie in Python über Paare in einer Liste (zirkulär)

Das Problem ist einfach. Ich möchte jedes Element der Liste und das nächste paarweise durchlaufen (wobei das letzte mit dem ersten umhüllt wird).

Ich habe über zwei unpythonische Methoden nachgedacht:

def pairs(lst):
    n = len(lst)
    for i in range(n):
        yield lst[i],lst[(i+1)%n]

und:

def pairs(lst):
    return Zip(lst,lst[1:]+[lst[:1]])

erwartete Ausgabe:

>>> for i in pairs(range(10)):
    print i

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
(9, 0)
>>> 

irgendwelche Vorschläge für eine pythonischere Vorgehensweise? Vielleicht gibt es eine vordefinierte Funktion, von der ich noch nichts gehört habe?

auch eine allgemeinere n-fache Version (mit Drillingen, Quartetten usw. anstelle von Paaren) könnte interessant sein.

35
fortran

Ich habe mir die allgemeinen Tuple-Versionen selbst geschrieben. Ich mag die erste, weil sie so einfach ist. Je mehr ich sie mir anschaue, desto pythonischer fühlt sie sich für mich an. Was ist pythonischer als ein One-Liner mit Zip? , Erweiterung von Sternchenargumenten, Listenverständnis, Aufteilen von Listen, Listenverkettung und "Bereich"?

def ntuples(lst, n):
    return Zip(*[lst[i:]+lst[:i] for i in range(n)])

Die itertools-Version sollte auch für große Listen effizient genug sein ...

from itertools import *
def ntuples(lst, n):
    return izip(*[chain(islice(lst,i,None), islice(lst,None,i)) for i in range(n)])

Und eine Version für nicht indizierbare Sequenzen:

from itertools import *
def ntuples(seq, n):
    iseq = iter(seq)
    curr = head = Tuple(islice(iseq, n))
    for x in chain(iseq, head):
        yield curr
        curr = curr[1:] + (x,)

Trotzdem vielen Dank für Ihre Vorschläge! :-)

9
fortran
def pairs(lst):
    i = iter(lst)
    first = prev = item = i.next()
    for item in i:
        yield prev, item
        prev = item
    yield item, first

Funktioniert mit jeder nicht leeren Sequenz, keine Indizierung erforderlich.

27

Ich mag wie immer Tee:

from itertools import tee, izip, chain

def pairs(iterable):
    a, b = tee(iterable)
    return izip(a, chain(b, [next(b)]))
6
pillmuncher

Dies könnte zufriedenstellend sein:

def pairs(lst):
    for i in range(1, len(lst)):
        yield lst[i-1], lst[i]
    yield lst[-1], lst[0]

>>> a = list(range(5))
>>> for a1, a2 in pairs(a):
...     print a1, a2
...
0 1
1 2
2 3
3 4
4 0

Wenn Sie solche Sachen mögen, schauen Sie sich Python-Artikel auf wordaligned.org an. Der Autor hat eine besondere Vorliebe für Generatoren in Python.

5
hughdbrown

Ich würde es so machen (hauptsächlich, weil ich das lesen kann):

class Pairs(object):
    def __init__(self, start):
        self.i = start
    def next(self):
        p, p1 = self.i, self.i + 1
        self.i = p1
        return p, p1
    def __iter__(self):
        return self

if __== "__main__":
    x = Pairs(0)
    y = 1
    while y < 20:
        print x.next()
        y += 1

gibt:

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
2
DrBloodmoney
[(i,(i+1)%len(range(10))) for i in range(10)]

ersetzen Sie den Bereich (10) durch die gewünschte Liste.

Im Allgemeinen ist die "zirkuläre Indizierung" in Python recht einfach. benutz einfach:

a[i%len(a)] 

Hier ist eine Version, die einen optionalen Startindex unterstützt (um beispielsweise (4, 0) als erstes Paar zurückzugeben, verwenden Sie start = -1:

import itertools

def iterrot(lst, start = 0):

    if start == 0:
        i = iter(lst)
    Elif start > 0:
        i1 = itertools.islice(lst, start, None)
        i2 = itertools.islice(lst, None, start)
        i = itertools.chain(i1, i2)
    else:
        # islice doesn't support negative slice indices so...
        lenl = len(lst)
        i1 = itertools.islice(lst, lenl + start, None)
        i2 = itertools.islice(lst, None, lenl + start)
        i = itertools.chain(i1, i2)
    return i


def iterpairs(lst, start = 0):

    i = iterrot(lst, start)     

    first = prev = i.next()
    for item in i:
        yield prev, item
        prev = item
    yield prev, first


def itertrios(lst, start = 0):

    i = iterrot(lst, start)     

    first = prevprev = i.next()
    second = prev = i.next()
    for item in i:
        yield prevprev, prev, item
        prevprev, prev = prev, item

    yield prevprev, prev, first
    yield prev, first, second
0
Jay Billfinger

So beantworten Sie Ihre Frage zur Lösung für den allgemeinen Fall:

import itertools

def pair(series, n):
    s = list(itertools.tee(series, n))
    try:
        [ s[i].next() for i in range(1, n) for j in range(i)]
    except StopIteration:
        pass
    while True:
        result = []
        try:
            for j, ss in enumerate(s):
                result.append(ss.next())
        except StopIteration:
            if j == 0:
                break
            else:
                s[j] = iter(series)
                for ss in s[j:]:
                    result.append(ss.next())
        yield result

Die Ausgabe sieht folgendermaßen aus:

>>> for a in pair(range(10), 2):
...     print a
...
[0, 1]
[1, 2]
[2, 3]
[3, 4]
[4, 5]
[5, 6]
[6, 7]
[7, 8]
[8, 9]
[9, 0]
>>> for a in pair(range(10), 3):
...     print a
...
[0, 1, 2]
[1, 2, 3]
[2, 3, 4]
[3, 4, 5]
[4, 5, 6]
[5, 6, 7]
[6, 7, 8]
[7, 8, 9]
[8, 9, 0]
[9, 0, 1]
0
hughdbrown

Noch kürzere Version der Zip * -Lösung von Fortran (diesmal mit Lambda;):

group = lambda t, n: Zip(*[t[i::n] for i in range(n)])

group([1, 2, 3, 3], 2)

gibt:

[(1, 2), (3, 4)]
0
MKTech

Natürlich können Sie immer ein deque verwenden:

from collections import deque
from itertools import *

def pairs(lst, n=2):
    itlst = iter(lst)
    start = list(islice(itlst, 0, n-1))
    deq = deque(start, n)
    for elt in chain(itlst, start):
        deq.append(elt)
        yield list(deq)
0
user3552819
def pairs(ex_list):
    for i, v in enumerate(ex_list):
        if i < len(list) - 1:
            print v, ex_list[i+1]
        else:
            print v, ex_list[0]

Enumerate gibt ein Tupel mit der Indexnummer und dem Wert zurück. Ich drucke den Wert und das folgende Element der Liste ex_list[i+1]. Die if i < len(list) - 1 bedeutet, wenn v nicht das letzte Mitglied der Liste ist. Wenn ja: print v und das erste Element der Liste print v, ex_list[0].

Bearbeiten:

Sie können dafür sorgen, dass eine Liste zurückgegeben wird. Hänge die gedruckten Tupel einfach an eine Liste an und gib sie zurück.

def pairs(ex_list):
    result = []
    for i, v in enumerate(ex_list):
        if i < len(list) - 1:
            result.append((v, ex_list[i+1]))
        else:
            result.append((v, ex_list[0]))
    return result
0
alexpinho98

Dies ist für gut oder schlecht unendlich zyklisch, algorithmisch jedoch sehr klar.

from itertools import tee, cycle

def nextn(iterable,n=2):
    ''' generator that yields a Tuple of the next n items in iterable.
    This generator cycles infinitely '''
    cycled = cycle(iterable)
    gens = tee(cycled,n)

    # advance the iterators, this is O(n^2)
    for (ii,g) in Zip(xrange(n),gens):
        for jj in xrange(ii):
            gens[ii].next()

    while True:
        yield Tuple([x.next() for x in gens])


def test():
    data = ((range(10),2),
        (range(5),3),
        (list("abcdef"),4),)
    for (iterable, n) in data:
        gen = nextn(iterable,n)
        for j in range(len(iterable)+n):
            print gen.next()            


test()

gibt:

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
(9, 0)
(0, 1)
(1, 2)
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
(3, 4, 0)
(4, 0, 1)
(0, 1, 2)
(1, 2, 3)
(2, 3, 4)
('a', 'b', 'c', 'd')
('b', 'c', 'd', 'e')
('c', 'd', 'e', 'f')
('d', 'e', 'f', 'a')
('e', 'f', 'a', 'b')
('f', 'a', 'b', 'c')
('a', 'b', 'c', 'd')
('b', 'c', 'd', 'e')
('c', 'd', 'e', 'f')
('d', 'e', 'f', 'a')
0
Gregg Lind