otro - Iterar sobre pares en una lista(moda circular) en Python
hacer ciclo for en python (13)
Aquí hay una versión que admite un índice de inicio opcional (por ejemplo, para devolver (4, 0) como el primer par, use 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
El problema es fácil, quiero iterar sobre cada elemento de la lista y el siguiente en pares (envolviendo el último con el primero).
He pensado en dos formas no epistónicas de hacerlo:
def pairs(lst):
n = len(lst)
for i in range(n):
yield lst[i],lst[(i+1)%n]
y:
def pairs(lst):
return zip(lst,lst[1:]+[lst[:1]])
Rendimiento esperado:
>>> 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)
>>>
alguna sugerencia sobre una forma más pythonic de hacer esto? tal vez hay una función predefinida de la que no he oído hablar?
también podría ser interesante una versión n-fold más general (con trillizos, cuartetos, etc. en lugar de pares).
Esto infinitamente ciclos, para bien o para mal, pero es algorítmicamente muy claro.
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()
da:
(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'')
Esto podría ser satisfactorio:
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 te gusta este tipo de cosas, mira los artículos de python en wordaligned.org . El autor tiene un amor especial por los generadores en python.
Lo haría así (sobre todo porque puedo leer esto):
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
da:
(0, 1)
(1, 2)
(2, 3)
(3, 4)
(4, 5)
(5, 6)
(6, 7)
(7, 8)
(8, 9)
Me he codificado las versiones generales de la tupla, me gusta la primera por su simplicidad elegante, cuanto más la miro, más pitónico me parece ... después de todo, lo que es más pitónico que un trazador de líneas con cremallera , extensión de argumento de asterisco, listas de comprensión, lista de división, lista de concatenación y "rango"?
def ntuples(lst, n):
return zip(*[lst[i:]+lst[:i] for i in range(n)])
La versión de itertools debería ser lo suficientemente eficiente incluso para listas grandes ...
from itertools import *
def ntuples(lst, n):
return izip(*[chain(islice(lst,i,None), islice(lst,None,i)) for i in range(n)])
Y una versión para secuencias no 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,)
De todos modos, ¡gracias a todos por sus sugerencias! :-)
Para responder a su pregunta sobre la resolución del caso general:
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
El resultado es así:
>>> 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]
Por supuesto, siempre puedes usar 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)
Versión aún más corta de la solución de rango zip * de Fortran (con lambda esta vez;):
group = lambda t, n: zip(*[t[i::n] for i in range(n)])
group([1, 2, 3, 3], 2)
da:
[(1, 2), (3, 4)]
Yo, como siempre, como T:
from itertools import tee, izip, chain
def pairs(iterable):
a, b = tee(iterable)
return izip(a, chain(b, [next(b)]))
[(i,(i+1)%len(range(10))) for i in range(10)]
reemplace el rango (10) con la lista que desea.
En general, la "indexación circular" es bastante fácil en Python; Solo usa:
a[i%len(a)]
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 devuelve una tupla con el número de índice y el valor. ex_list[i+1]
el valor y el siguiente elemento de la lista ex_list[i+1]
. El if i < len(list) - 1
significa si v no es el último miembro de la lista. Si es así: imprima v y el primer elemento de la lista print v, ex_list[0]
.
Editar:
Puedes hacer que devuelva una lista. Solo agregue las tuplas impresas a una lista y devuelvala.
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
def pairs(lst):
i = iter(lst)
first = prev = item = i.next()
for item in i:
yield prev, item
prev = item
yield item, first
Funciona en cualquier secuencia no vacía, no se requiere indexación.
i=(range(10))
for x in len(i):
print i[:2]
i=i[1:]+[i[1]]
más pitónico que esto es imposible