Le problème est facile, je veux parcourir chaque élément de la liste et le suivant par paires (en enveloppant le dernier avec le premier).
J'ai réfléchi à deux manières non pythoniques 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]])
production attendue:
>>> 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 le faire? Peut-être existe-t-il une fonction prédéfinie dont je n'ai pas entendu parler?
une version plus générale à plis multiples (avec triplets, quatuors, etc. au lieu de paires) pourrait également être intéressante.
J'ai moi-même codé les versions générales de Tuple, j'aime bien la première pour sa simplicité, plus je la regarde, plus elle me semble pythonique ... après tout, quoi de plus pythonique qu'un liner avec Zip , expansion de l'argument astérisque, compréhension de la liste, découpage de la liste, concaténation de la liste et "plage"?
def ntuples(lst, n):
return Zip(*[lst[i:]+lst[:i] for i in range(n)])
La version d'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,)
Quoi qu'il en soit, merci à tous pour vos suggestions! :-)
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.
Comme toujours, j'aime le tee:
from itertools import tee, izip, chain
def pairs(iterable):
a, b = tee(iterable)
return izip(a, chain(b, [next(b)]))
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, consultez les articles en python sur wordaligned.org . L'auteur a un amour particulier pour les générateurs en python.
Je le ferais comme ceci (principalement 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 __== "__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)
[(i,(i+1)%len(range(10))) for i in range(10)]
remplacez la plage (10) par la liste souhaitée.
En général, "l'indexation circulaire" est assez facile en python; juste utiliser:
a[i%len(a)]
Voici une version qui prend en charge un index de démarrage facultatif (par exemple, pour renvoyer (4, 0) en tant que 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
Pour répondre à votre question sur la résolution du problème 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 ça:
>>> 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]
Version encore plus courte de la solution Zip * 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)]
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)
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 renvoie un tuple avec le numéro d'index et la valeur. J'imprime la valeur et l'élément suivant de la liste ex_list[i+1]
. Le if i < len(list) - 1
signifie que si v est not le dernier membre de la liste. Si c'est le cas: print v et le premier élément de la liste print v, ex_list[0]
.
Vous pouvez le faire retourner une liste. Ajoutez simplement les n-uplets imprimés à une liste et renvoyez-la.
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
Cet infini cycle, en bien ou en mal, mais est très clair algorithmiquement.
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')