Recursión usando rendimiento


82

¿Hay alguna forma de mezclar la recursividad y la yielddeclaración? Por ejemplo, un generador de números infinitos (usando recursividad) sería algo como:

def infinity(start):
    yield start
    # recursion here ...

>>> it = infinity(1)
>>> next(it)
1
>>> next(it)
2

Lo intenté:

def infinity(start):
    yield start
    infinity(start + 1)

y

def infinity(start):
    yield start
    yield infinity(start + 1)

Pero ninguno de ellos hizo lo que yo quería, el primero se detuvo después de que cedió starty el segundo cedió start, luego el generador y luego se detuvo.

NOTA: Por favor, sé que puede hacer esto usando un bucle while:

def infinity(start):
    while True:
        yield start
        start += 1

Solo quiero saber si esto se puede hacer de forma recursiva.


Vea [aquí] [1] para una buena respuesta a esta pregunta que planteé hace un tiempo. [1]: stackoverflow.com/questions/5704220/…
sizzzzlerz

Nota: la forma correcta de hacer esto sería usar en itertools.countlugar de lanzar su propia solución, basada en bucles u otra.
Petr Viktorin

8
@PetrViktorin esto es solo un ejemplo, generar números infinitos no es en absoluto el problema real
juliomalegria

Respuestas:


154

Sí, usted puede hacer esto:

def infinity(start):
    yield start
    for x in infinity(start + 1):
        yield x

Sin embargo, esto generará un error una vez que se alcance la profundidad máxima de recursividad.

A partir de Python 3.3, podrá utilizar

def infinity(start):
    yield start
    yield from infinity(start + 1)

Si simplemente llama a su función de generador de forma recursiva sin hacer un bucle sobre ella o yield from-hacerla, todo lo que debe hacer es construir un nuevo generador, sin ejecutar el cuerpo de la función ni generar nada.

Consulte PEP 380 para obtener más detalles.


12
Pero parece yield fromque todavía hay un límite de recursividad :(
Jo So

2
siempre habrá un límite de recursividad
Radio controlado

12

En algunos casos, puede ser preferible utilizar una pila en lugar de la recursividad para los generadores. Debería ser posible reescribir un método recursivo usando una pila y un bucle while.

Aquí hay un ejemplo de un método recursivo que usa una devolución de llamada y se puede reescribir usando lógica de pila:

def traverse_tree(callback):
    # Get the root node from somewhere.
    root = get_root_node()
    def recurse(node):
        callback(node)
        for child in node.get('children', []):
            recurse(child)
    recurse(root)

El método anterior atraviesa un árbol de nodos donde cada nodo tiene una childrenmatriz que puede contener nodos secundarios. A medida que se encuentra cada nodo, se emite la devolución de llamada y se le pasa el nodo actual.

El método podría usarse de esta manera, imprimiendo alguna propiedad en cada nodo.

def callback(node):
    print(node['id'])
traverse_tree(callback)

Use una pila en su lugar y escriba el método transversal como un generador

# A stack-based alternative to the traverse_tree method above.
def iternodes():
    stack = [get_root_node()]
    while stack:
        node = stack.pop()
        yield node
        for child in reversed(node.get('children', [])):
            stack.append(child)

(Tenga en cuenta que si desea el mismo orden transversal que originalmente, debe invertir el orden de los hijos porque el primer hijo agregado a la pila será el último en aparecer).

Ahora puede obtener el mismo comportamiento que el traverse_treeanterior, pero con un generador:

for node in iternodes():
    print(node['id'])

Esta no es una solución única para todos, pero para algunos generadores puede obtener un buen resultado sustituyendo el procesamiento de pila por la recursividad.


3
¡Buena respuesta! El rendimiento en Python 2.7 realmente no se puede usar con recursividad, pero al administrar manualmente la pila, puede obtener el mismo efecto.
00prometheus

0
def lprint(a):
    if isinstance(a, list):
        for i in a:
            yield from lprint(i)
    else:
        yield a

b = [[1, [2, 3], 4], [5, 6, [7, 8, [9]]]]
for i in lprint(b):
    print(i)

¿Qué es b? Trate de no dejar respuestas de solo código ... Una pequeña aclaración y explicación ayudarán a poner las cosas en contexto y comprender mejor su respuesta
Tomerikoo

para i en lprint (a): print (i)
Юрий Блинков

¿Por qué no editar la respuesta para que quede más clara? Puede hacerlo haciendo clic en la pequeña editetiqueta debajo de su respuesta o haciendo clic aquí . Además, como dije, intente agregar una pequeña explicación de cómo y por qué esto resuelve el problema
Tomerikoo

-3

Entonces, básicamente, solo necesita agregar un bucle for en el lugar donde necesita llamar a su función de forma recursiva . Esto se aplica a Python 2.7.


1
aunque esta respuesta requiere más detalles, en realidad está en línea con la respuesta aceptada de Sven Marnach, vea su primer fragmento de código ...
Coffee_Table
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.