2010-08-17 15 views
5

Una cuestión de particular interés sobre python para bucles. Los programas de ingeniería a menudo requieren valores en los índices anteriores o futuros, tales como:¿Es posible hacer referencia a entradas de lista dentro de un bucle for sin índices?

for i in range(0,n): 
    value = 0.3*list[i-1] + 0.5*list[i] + 0.2*list[i+1] 

etc ...

Sin embargo me gusta más el buen pitón sintaxis limpia:

for item in list: 
    #Do stuff with item in list 

o para una lista de datos de 2d puntos:

for [x,y] in list: 
    #Process x, y data 

Me gusta el concepto de bucle sobre una lista sin explícitamente usando un índice para referenciar los artículos en la lista. Me preguntaba si había una forma clara de tomar el artículo anterior o siguiente sin pasar el índice (o sin hacer un seguimiento del índice de forma independiente).

EDIT:

Gracias Andrew Jaffe (y por poder de Mark Byers) y gnibbler para los ejemplos simples y extensibles. Hasta ahora no estaba al tanto de los itertools o nwise modules. John Machin: gracias por el muy complejo ejemplo de lo que NO debes hacer. Pones mucho esfuerzo en este ejemplo, obviamente el algoritmo algo recursivo que presenté no puede producir una lista con el mismo número de elementos que la lista de entrada y presenta problemas si no se utilizan índices explícitos. Un algoritmo como este comúnmente ocurriría en el procesamiento de la señal.

Respuesta

4

He aquí una receta, basado en el código de pares itertools, lo que lo hace n-sabia grupo general:

import itertools 

def nwise(iterable, n=2): 
    "s->(s_0,s_1, ..., s_n), (s_1,s_2,..., s_n+1), ... " 
    ntup = itertools.tee(iterable, n) 
    for i, item in enumerate(ntup): 
     for ii in range(i): 
      next(item, None) 
    return itertools.izip(*ntup) 

que puede ser utilizado de esta manera:

>>> import nwise 
>>> ll = range(10) 
>>> for tup in nwise.nwise(ll,3): print tup 
... 
(0, 1, 2) 
(1, 2, 3) 
(2, 3, 4) 
(3, 4, 5) 
(4, 5, 6) 
(5, 6, 7) 
(6, 7, 8) 
(7, 8, 9) 

[Gracias a la respuesta de Mark Byers para la idea]

2

Para tener acceso a un elemento y el siguiente se puede utilizar el dos a dos recipe que se muestra en la itertools documentation:

def pairwise(iterable): 
    "s -> (s0,s1), (s1,s2), (s2, s3), ..." 
    a, b = tee(iterable) 
    next(b, None) 
    return izip(a, b) 

Esto podría ser adaptado para permitir el acceso a los tres elementos vecinos en lugar de dos.

1
>>> from itertools import islice, izip 
>>> seq = range(10) 
>>> for x,y,z in izip(*(islice(seq,i,None) for i in range(3))): 
... print x,y,z 
... 
0 1 2 
1 2 3 
2 3 4 
3 4 5 
4 5 6 
5 6 7 
6 7 8 
7 8 9 

esto se puede ampliar trivialmente más allá de 3 elementos.

Si lo necesita para trabajar con cualquier iterable, la respuesta de Andrew es adecuado, o puede hacerlo de esta manera

>>> from itertools import izip, islice, tee 
>>> seq=(x for x in range(10)) 
>>> for x,y,z in izip(*(islice(j,i,None) for i,j in enumerate(tee(seq,3)))): 
... print x,y,z 
... 
0 1 2 
1 2 3 
2 3 4 
3 4 5 
4 5 6 
5 6 7 
6 7 8 
7 8 9 
0

Si desea utilizar postal, así es como que no debe hacerse (rellenas, sin necesidad) y cómo no verificarlo (relleno x 2.5), con esas deficiencias corregidas.

vector = [2**i for i in range(1,6)] 
print "vector", vector 
value = [] 
for i in range(1,len(vector)-1): 
    value.append(0.3*vector[i-1] + 0.5*vector[i] + 0.2*vector[i+1]) 

print "value", len(value), value 
value2=[0.3*before + 0.5* this + 0.2 * after 
      for before,this,after in zip(vector,vector[1:]+[0], vector[2:]+[0,0]) 
      ] 
# above +[0] and +[0,0] needlessly extend the answer by two items 
print "value2", len(value2), value2   
print "bad check bad values", not any([x-y for x,y in zip(value,value2) if x-y > 1e-7]) 
# the bad check doesn't check for length 
# the bad check doesn't use abs(actual - expected) 
# the bad check has a unnecessary if test in it 
# the bad check uses a list comprehension when a generator would do 
print "good check bad values", (
    len(value2) == len(value) 
    and 
    not any(abs(x-y) > 1e-7 for x,y in zip(value,value2)) 
    ) 
value2=[0.3*before + 0.5* this + 0.2 * after 
      for before,this,after in zip(vector,vector[1:], vector[2:]) 
      ]  
print "fixed value2", len(value2), value2 
print "good check good values", (
    len(value2) == len(value) 
    and 
    not any(abs(x-y) > 1e-7 for x,y in zip(value,value2)) 
    ) 
+0

Esta solución sería correcta si se supone que el vector cambia de longitud y el cálculo solo se usa para aquellos valores que tienen ambos vecinos. Los multiplicadores en el ejemplo parecen resumir 1.0, y 0 no es realmente el valor del vecino para los primeros y últimos números de los vectores. Además, el ejemplo se completa para durar como vecino en caso de primer valor es cuestionable, sin embargo –

Cuestiones relacionadas