Itérer sur les paires dans une liste (circulaires) en Python

le problème est facile, je veux itérer sur chaque élément de la liste et le suivant en paires (enveloppant le dernier avec le premier).

j'ai réfléchi à deux façons impythoniques de le faire:

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

et:

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

résultats escomptés:

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

des suggestions sur une façon plus pythonique de faire cela? peut-être qu'il y a une fonction prédéfinie là-bas je n'ai pas entendu ?

aussi un n-fold plus général (avec triplets, quatuors, etc. au lieu de paires) la version pourrait être intéressante.

35
demandé sur Colonel Panic 2009-08-11 01:50:38

13 réponses

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

fonctionne sur toute séquence non vide, aucune indexation requise.

27
répondu Martin v. Löwis 2012-02-19 23:17:33

j'ai codé moi-même les versions générales de tuple, j'aime la première pour sa simplicité élégante, plus je la regarde, plus Pythonic il se sent pour moi... après tout, qu'est-ce qui est plus Pythonic qu'un liner unique avec zip, l'extension d'argument asterisk, les compréhensions de liste, le découpage de liste, la concaténation de liste et la "portée"?

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

la version itertools devrait être assez efficace même pour les grandes listes...

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

et une version pour les séquences non indexables:

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

en tout cas, merci à tous pour vos suggestions! :- )

9
répondu fortran 2011-10-24 17:31:47

j'ai, comme toujours, comme en té:

from itertools import tee, izip, chain

def pairs(iterable):
    a, b = tee(iterable)
    return izip(a, chain(b, [next(b)]))
6
répondu pillmuncher 2012-03-27 07:32:21

cela pourrait être satisfaisant:

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

si vous aimez ce genre de choses, regardez les articles de python sur wordaligned.org . L'auteur a un amour particulier pour les générateurs en python.

5
répondu hughdbrown 2009-08-10 22:00:18

je le ferais comme ceci (surtout parce que je peux lire ceci):

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 __name__ == "__main__":
    x = Pairs(0)
    y = 1
    while y < 20:
        print x.next()
        y += 1

donne:

(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
2
répondu DrBloodmoney 2009-08-10 23:08:30
[(i,(i+1)%len(range(10))) for i in range(10)]

remplacer la portée (10) par la liste que vous voulez.

en général "indexation circulaire" est assez facile en python; il suffit d'utiliser:

a[i%len(a)] 
1
répondu Jose Antonio Martin H 2011-06-05 10:05:32

pour répondre à votre question sur la résolution pour le cas général:

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

la sortie est comme ceci:

>>> 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
répondu hughdbrown 2009-08-10 23:11:14

cela tourne à l'infini, pour le bien ou le mal, mais est algorithmiquement très clair.

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

donne:

(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
répondu Gregg Lind 2009-08-11 00:42:37

version encore plus courte de la solution zip * range de Fortran (avec lambda cette fois;):

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

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

donne:

[(1, 2), (3, 4)]
0
répondu MKTech 2010-02-09 19:05:51

Voici une version qui supporte un index de démarrage optionnel (par exemple pour retourner (4, 0) comme première paire, utilisez 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
répondu Jay Billfinger 2011-04-10 18:44:48
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 retourne un tuple avec l'indice et la valeur. J'imprime la valeur et l'élément suivant de la liste ex_list[i+1] . Le si i < len(list) - 1 signifie que v est et non le dernier membre de la liste. Si c'est: Imprimer v et le premier élément de la liste print v, ex_list[0] .

Edit:

vous pouvez lui faire retourner une liste. Il suffit d'ajouter les tuples imprimés à une liste et de le retourner.

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
répondu alexpinho98 2013-05-15 15:41:28

bien sûr, vous pouvez toujours utiliser un deque :

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
répondu user3552819 2014-04-19 23:07:20
i=(range(10))

for x in len(i):
    print i[:2]
    i=i[1:]+[i[1]]

plus pythonique que cela est impossible

-1
répondu Malkyus 2011-01-04 01:59:53