agregando generadores a una pila en bucle, los generadores apuntan a la variable de bucle final

Estoy haciendo un recorrido gráfico. En cada punto guardo un generador de las otras posibles opciones que se podrían haber explorado. Más tarde, exploro algunos de esos generadores pero no funciona.

Aquí hay un ejemplo simplificado donde puede ver que la variable "nodo" está configurada en 3 en todos los generadores. (por lo que los generadores apuntan de nuevo a la variable "nodo", pero la variable "nodo" cambia antes de que se consuma el generador.

En mi caso particular, puedo almacenar algunos punteros y agregar la lógica de qué hacer con esos punteros para volver a crear el generador, pero esta es una solución fea.

¿Existe una forma sencilla de hacerlo?

node_size = {1:1, 2:2, 3:1, 4:3}
iters = []
for node in range(1,4):
    it = (1 + node_size[node]+j for j in xrange(3))
    #it = iter(list(it)) #remove comment to get correct result but very slow.
    iters.append(it)

for iter_ in iters:
    print list(iter_)

"""
Correct Output
[2, 3, 4]
[3, 4, 5]
[2, 3, 4]
"""

"""
Actual Output:
[2, 3, 4]
[2, 3, 4]
[2, 3, 4]
"""

preguntado el 11 de abril de 13 a las 04:04

Mi mejor solución actual es usar itertools.repeat -

2 Respuestas

Su expresión generadora hace referencia a la variable global node. Dado que esta es una variable libre en el genex, se cierra sobre el nombre , no el valor. Cada vez que toma un elemento del generador, la expresión 1 + node_size[node]+j se evalúa con el corriente valor de node. Es decir, el valor de node se lee cada vez que se avanza el generador, no de una vez por todas en el momento de su creación. En el momento en que comience a tomar elementos del generador, node es 3, por lo que todos los elementos del generador reflejan ese valor.

Para conseguir lo que quieres, necesitas unir node en la propia función generadora. Una forma rápida de hacer esto es forzar node en la porción de bucle del genex:

it = (1 + node_size[node]+j for node in [node] for j in xrange(3))

Dado que la parte del bucle se evalúa solo una vez cuando se crea el genex, esto corrige un valor único de node en el ámbito genex.

Si esa forma le parece demasiado fea, tendrá que escribir una función generadora explícita en lugar de usar un genex:

def gen(nodeVal):
    for j in xrange(3):
        yield 1 + node_size[nodeVal]+j
for node in range(1, 4):
    iters.append(gen(node))

Aquí el generador se cierra sobre el nombre nodeVal, pero dado que cada generador se crea mediante una llamada de función separada, cada uno obtiene su propio valor de nodeVal y todo va bien.

Respondido 11 Abr '13, 04:04

Está funcionando: explore = ((s - E, e, v) for pos, E, c in [(pos, E, children)] for s, e, v in (c[i] for i in xrange(pos, len(c)))) - robo oxidado

¿Qué tal hacer funciones generadoras que se cierren sobre el valor del tamaño del nodo?

node_size = {1:1, 2:2, 3:1, 4:3}

iters = []
for node in xrange(1, 4):
    def it(n=node_size[node]):
        for j in xrange(1, 4):
            yield n + j
    itr = it()
    iters.append(itr)

for iter_ in iters:
    print list(iter_)

Esto imprime el resultado correcto para mí.

EDITAR: @BrenBarn publicó una respuesta que me llevó directamente a esta respuesta:

node_size = {1:1, 2:2, 3:1, 4:3}

iters = []
for node in range(1, 4):
    n = node_size[node]
    itr = xrange(n+1, n+4)
    iters.append(itr)

for iter_ in iters:
    print list(iter_)

Cuando usted llama xrange() evalúa sus argumentos y luego te devuelve un iterador que arroja los números.

¡No creo que haya una forma más eficiente de hacer esto en Python!

En este caso, pudimos evitar todas las matemáticas y obtener xrange() para producir exactamente los números deseados. Si realmente necesitaba evaluar una expresión, aún puede hacerlo de la manera del generador de expresiones:

itr = (1+j for j in xrange(n, n+3))

Respondido 11 Abr '13, 05:04

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.