Sé lo que es la recursividad (cuando un patten vuelve a aparecer dentro de sí mismo, generalmente una función que se llama a sí misma en una de sus líneas, después de un rompimiento condicional ... ¿verdad?), Y puedo entender las funciones recursivas si las estudio detenidamente. Mi problema es que cuando veo nuevos ejemplos, siempre estoy inicialmente confundido. Si veo un bucle, o un mapeo, compresión, anidamiento, llamadas polimórficas, etc., sé lo que sucede con solo mirarlo. Cuando veo código recursivo, mi proceso de pensamiento suele ser '¿qué es esto?' seguido de 'oh, es recursivo' seguido de 'Supongo que debe funcionar, si dicen que sí'.
Entonces, ¿tiene algún consejo / plan / recurso para desarrollar habilidades en esta área? La recursión es una especie de concepto extraño, así que creo que la forma de abordarla puede ser igualmente extraña e inmune.
Respuestas:
Comience con algo simple y tracéelo con lápiz y papel. En serio. Un buen lugar para comenzar son los algoritmos de recorrido de árbol, ya que se manejan mucho más fácilmente usando la recursión que la iteración regular. No tiene que ser un ejemplo complicado, sino algo simple y con lo que puedas trabajar.
Sí, es extraño y a veces contra-intuitivo, pero una vez que hace clic, una vez que dices "¡Eureka!" ¡te preguntarás cómo no lo entendiste antes! ;) Sugerí árboles porque son (IMO) la estructura más fácil de entender en la recursividad, y son fáciles de trabajar con un lápiz y papel. ;)
fuente
Recomiendo encarecidamente Scheme, utilizando el libro The Little Lisper. Una vez que haya terminado con él, usted va a entender la recursividad, en el fondo. Casi garantizado
fuente
Definitivamente sugiero SICP. Además, debe consultar los videos del curso introductorio de los autores aquí ; son increíblemente alucinantes.
Otro camino, no tan estrictamente relacionado con la programación, es leer Gödel, Escher, Bach: an Eternal Golden Braid de Hofstadter. Una vez que lo superes, la recursión se verá tan natural como la aritmética. Además, estará convencido de que P = nP y de que querrá construir máquinas pensantes, pero es un efecto secundario que es tan pequeño en comparación con los beneficios.
fuente
Esencialmente, todo se reduce a la práctica ... Tome los problemas generales (clasificación, búsqueda, problemas matemáticos, etc.) y vea si puede ver una forma de resolver esos problemas si aplica una sola función varias veces.
Por ejemplo, la ordenación rápida funciona porque mueve el elemento de una lista en dos mitades y luego se aplica nuevamente a cada una de esas mitades. Cuando ocurre la clasificación inicial, no está preocupado por ordenar las dos mitades en ese punto. Más bien, toma el elemento pivote y coloca todos los elementos más pequeños que ese elemento en un lado y todos los elementos más grandes o iguales que en el otro lado. ¿Tiene sentido cómo puede llamarse recursivamente en ese punto para ordenar las dos nuevas listas más pequeñas? También son listas. Solo más pequeño. Pero aún necesitan ser ordenados.
El poder detrás de la recursión es la noción de dividir y conquistar. Divide un problema repetidamente en problemas más pequeños que son idénticos en naturaleza pero solo más pequeños. Si haces eso lo suficiente, eventualmente llegas a un punto en el que la única pieza restante ya está resuelta, entonces simplemente sales del bucle y el problema está resuelto. Estudie los ejemplos que mencionó hasta que los entienda . Puede tomar un tiempo, pero eventualmente será más fácil. ¡Entonces trata de resolver otros problemas y haz una función recursiva para resolverlos! ¡Buena suerte!
EDITAR: También debo agregar que un elemento clave para la recursión es la capacidad garantizada de la función para poder detenerse. Esto significa que la ruptura hacia abajo del problema original deben obtener continuamente más pequeño y, finalmente, es necesario que haya un punto de parada garantizada (un punto en el que el nuevo problema es secundario, ya sea resoluble o ya resuelto).
fuente
def factorial(number): """return factorial of number""" if number == 0: return 0 elif number == 1: return 1 else: return number * factorial(number - 1)
Personalmente creo que tu mejor apuesta es a través de la práctica.
Aprendí la recursividad con LOGO. Podrías usar LISP. La recursión es natural en esos idiomas. De lo contrario, puede compararlo con el estudio de series y series matemáticas donde expresa lo que sigue a partir de lo que vino antes, es decir, u (n + 1) = f (u (n)), o series más complejas donde tiene múltiples variables y dependencias múltiples, por ejemplo, u (n) = g (u (n-1), u (n-2), v (n), v (n-1)); v (n) = h (u (n-1), u (n-2), v (n), v (n-1)) ...
Por lo tanto, mi sugerencia sería que encuentre "problemas" de recursión estándar simples (en su expresión) e intente implementarlos en el idioma que elija. La práctica lo ayudará a aprender a pensar, leer y expresar esos "problemas". Tenga en cuenta que a menudo algunos de estos problemas se pueden expresar a través de la iteración, pero la recursión podría ser una forma más elegante de resolverlos. Uno de ellos es el cálculo de números factoriales.
Los "problemas" gráficos que encuentro hacen que sea más fácil de ver. Así que busque los copos de Koch, Fibonacci, la curva del dragón y los fractales en general. Pero también mira el algoritmo de clasificación rápida ...
Debe bloquear algunos programas (bucles interminables, uso tentativo de recursos infinitos) y manejar mal las condiciones finales (para obtener resultados inesperados) antes de entenderlo todo. E incluso cuando lo obtenga, seguirá cometiendo esos errores, con menos frecuencia.
fuente
Estructura e interpretación de programas de computadora
Es el libro utilizado para el aprendizaje, no solo de recursión, sino de programación en general en varias universidades. Es uno de esos libros fundamentales que proporciona más información cuanto más experiencia ganes y más lo leas.
fuente
Por mucho que me guste SICP y Gödel, Escher, Bach: una eterna trenza dorada , el LISP de Touretzky : una introducción suave a la computación simbólica también hace un buen trabajo al introducir la recursividad.
El concepto básico es el siguiente: primero, debe saber cuándo finaliza su función recursiva, para que pueda devolver un resultado. Luego, debe saber cómo tomar el caso inacabado y reducirlo a algo en lo que pueda recurrir. Para el ejemplo factorial tradicional (N), ha terminado cuando N <= 1, y el caso sin terminar es N * factorial (N-1).
Para un ejemplo mucho más feo, está la función A de Ackermann (m, n).
fuente
Sugiero jugar con algunos lenguajes funcionales de estilo ML como OCaml o Haskell. Descubrí que la sintaxis de coincidencia de patrones realmente me ayudó a comprender incluso funciones recursivas relativamente complicadas, ciertamente mucho mejores que las de Scheme
if
y lascond
declaraciones. (Aprendí Haskell y Scheme al mismo tiempo).Aquí hay un ejemplo trivial para el contraste:
y con coincidencia de patrones:
Este ejemplo realmente no hace justicia a la diferencia: nunca tuve problemas con ninguna de las versiones de la función. Es solo para ilustrar cómo son las dos opciones. Una vez que llega a funciones mucho más complejas, usando cosas como listas y árboles, la diferencia se vuelve mucho más pronunciada.
Recomiendo especialmente Haskell porque es un lenguaje simple con una sintaxis muy agradable. También hace mucho más fácil trabajar con ideas más avanzadas como corecursion :
(No entenderá el código anterior hasta que juegue un poco con Haskell, pero tenga la seguridad de que es básicamente mágico: P.) Seguro que podría hacer lo mismo con las transmisiones en Scheme, pero es mucho más natural en Haskell.
fuente
Está agotado, pero si puede encontrarlo, "Algoritmos recursivos" de Richard Lorentz no es más que recurrencia. Cubre los conceptos básicos de recursividad, así como algoritmos recursivos específicos.
Los ejemplos están en Pascal, pero no son tan grandes que la elección del idioma sea molesta.
fuente