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.
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! :-)
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.
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)]))
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.
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)
[(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
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]
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)]
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)
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]
.
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
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')