¿Es la recursión una instancia de ser "demasiado inteligente" al programar?

8

Leí varios libros y aprendí a través de la experiencia que optimizar el código hasta el punto en que es inescrutable, o encontrar una solución extremadamente rápida pero extremadamente compleja a un problema no es deseable cuando se trabaja en equipo, o incluso cuando se está trabajando. solo y tiene que entender su solución inteligente algún tiempo después.

Mi pregunta es, ¿debería tratarse la recursión de la misma manera? ¿El programador promedio entiende la recursión fácilmente y, por lo tanto, uno debería usarla con impunidad, o el programador promedio no comprende muy bien la recursión y debería alejarse de ella por el bien de la productividad general del equipo?

Sé que hay respuestas simples de "Cualquier programador que no entienda la recursión no vale la pena, así que no se preocupe por ellos", pero me preguntaba si todos ustedes tendrían alguna experiencia en el mundo real que les gustaría compartir eso iluminaría el tema más que la opinión que acabo de mencionar.

Abadía de Macy
fuente
2
Esta pregunta es bastante similar a programmers.stackexchange.com/questions/24997/… que también se hizo hoy. Algunas buenas respuestas allí.
Nicole
44
¿Cómo puedes ser un programador promedio si no entiendes la recursividad? (tenga en cuenta la diferencia de "cualquier programador")

Respuestas:

22

¿El programador promedio entiende la recursión fácilmente y, por lo tanto, uno debería usarla con impunidad, o el programador promedio no comprende muy bien la recursión y debería alejarse de ella por el bien de la productividad general del equipo?

Yo diría que el programador promedio entiende perfectamente la recursividad. De hecho, si el programador ha obtenido un título en Ciencias de la Computación o Ingeniería de Software, está prácticamente garantizado. Por supuesto, hay algunos programadores muy por debajo del promedio, pero no los quieres en tu equipo.

En este caso, la distinción entre un promedio y un buen programador es saber cuándo usar la recursión y cuándo no. Y eso depende del problema que se resuelva Y del lenguaje utilizado para resolverlo.

  • Si está utilizando un lenguaje de programación funcional, la recursividad es una solución natural y eficiente para una amplia gama de problemas. (¡Reglas de optimización de recursión de cola!)

  • Si está utilizando un OO o un lenguaje de procedimiento simple, la recursión puede ser ineficiente y puede ser problemática debido a desbordamientos de la pila. Entonces, en algunos casos, elegiría una solución iterativa en lugar de una recursiva. Sin embargo, en otros casos, la solución recursiva es mucho más simple y elegante que la solución iterativa (posiblemente más eficiente) sería la "demasiado inteligente". (Por ejemplo, si el problema requiere retroceder, construir o caminar árboles / gráficos, etc., la recursión es a menudo más simple).

Stephen C
fuente
1
+1 Para comentarios de recursión de cola. A menudo, un intento de eliminar la recursión termina con muchas estructuras de pila y bucles en su código, que replican efectivamente el componente de pila de llamadas.
Orbling
1
@Orbling: eso es cierto, pero es probable que una pila implementada utilizando una matriz o una lista de matrices sea más escalable que la pila de llamadas de un subproceso. Especialmente porque las pilas de llamadas de hilo no pueden crecer en muchas implementaciones de lenguaje.
Stephen C
Sí, es un problema eterno del diseño de funciones recursivas. Más bien un problema para los diseñadores de lenguaje que el programador, idealmente.
Orbling
"De acuerdo, hay algunos programadores muy por debajo del promedio ": OK, solo hay que decirlo ... Por definición, la mitad de los programadores están por debajo del promedio y la mitad de ellos están "muy por debajo" del promedio.
Lawrence Dol
@LawrenceDol: nunca he visto a alguien afirmar que "muy" significa la mitad de nada. ¿Cuál es su fuente para su definición? (Esto también tiene que ser pedido, debido a la pedantería solamente tiene ningún valor si se basaba de hecho.)
Stephen C
42

Algunos problemas son naturalmente recursivos. Encontrar una solución iterativa en estos casos en realidad puede ser más torpe y complejo que los recursivos. Un buen ejemplo es cualquier algoritmo que necesite atravesar una estructura de árbol jerárquica, que es una tarea común en la programación.

TL; versión DR: No.

Mesas Bobby
fuente
99
Convenido. Es más peligroso etiquetar las cosas como "dañinas" y llegar a los extremos para evitarlas, lo que siempre terminará siendo más confuso y difícil de seguir adelante.
heretik
11

La recursión es un principio fundamental en la mayoría de los lenguajes de programación funcionales. La iteración (bucle) en lenguajes funcionales generalmente se logra a través de la recursividad.

Los lenguajes funcionales han visto un renacimiento últimamente, debido a la necesidad de manejar con elegancia más núcleos de procesador; los lenguajes funcionales ayudan a lograr este tipo de concurrencia al proporcionar formas de razonar mejor sobre su programa sin la complejidad que implica bloquear estructuras mutables.

Robert Harvey
fuente
+1 Buen comentario. Aunque no estoy seguro de que "un renacimiento" tenga toda la razón, o la razón de los núcleos múltiples. Siempre han sido un paradigma favorito de aquellos que los conocían, pero su proliferación en la corriente principal nunca fue grande. Los lenguajes funcionales se han vuelto más convencionales, tomaría todo un hilo para repasar esa cuestión. ( programmers.stackexchange.com/questions/20036/… que ya has leído)
Orbling
11

Algunos problemas, como caminar por la estructura de un árbol (caminar, por ejemplo, una estructura de directorio completa, en lugar de, por ejemplo, buscar un nodo B-Tree específico), son ideales para usar la recursividad; los equivalentes no recursivos a menudo simplemente agregan la complicación de administrar su propia pila.

En estos casos, la recursión es la mejor solución, la más simple y la más fácil de entender.

Lawrence Dol
fuente
7

Yo diría que use la recursividad donde sea apropiado, pero siempre busque formas de evitar la recursividad explícita.

Por ejemplo, si encuentra la necesidad de atravesar manualmente una estructura de árbol, entonces sí, debería usar la recursividad. Si alguien es lo suficientemente tonto como para no entender un recorrido recursivo del árbol, no harán ni la cabeza ni la cola de la versión iterativa.

Pero una mejor opción es usar un iterador que abstraiga la recursión hasta el punto en que todo lo que ve es "Realizo esta operación en todo el árbol".

Luego.
fuente
1
Cierto (y +1), pero, por supuesto, alguien tiene que escribir la abstracción.
Lawrence Dol
1
Como dice @Software Monkey, todavía debe codificarse de forma recursiva, pero la abstracción a un iterador para "usar" está bien.
Orbling
1
+1 para el punto de que alguien que no entendería la versión recursiva probablemente no entenderá la versión iterativa. Desafortunadamente, esa persona tampoco se daría cuenta de que no entiende la versión iterativa.
Larry Coleman
3

La recursividad es el mecanismo más simple desde el punto de vista de la limpieza del código. Si la velocidad es absolutamente esencial, entonces quizás pueda usar una matriz 2D de parámetros problemáticos. Engendrar un proceso hijo es simplemente agregar otro elemento a la matriz. Una vez hice un solucionador tridiagonal de ensamblador, eso fue un gran problema en el pasado. El contexto necesario por instancia era de 8 palabras por nivel, y cada subproblema era un tercio del tamaño del anterior. Esta "biblioteca" solo era popular porque superaba a todas las demás implementaciones. Pero, esa es una situación bastante rara en la programación, por lo que no necesita sucumbir a la "optimización prematura", simplemente porque esta solución está disponible. Obviamente, por algunas cosas, es una exageración terrible, como el ejemplo de recursión 101 "calcular el factorial". Pero para la mayoría de las aplicaciones,

Tengo un simple corrector ortográfico que uso para una aplicación (donde quiero dar pistas sobre cómo corregir errores ortográficos menores), donde calculo la "distancia" entre dos cadenas, permitiendo que se permitan eliminaciones y adiciones. Esto conduce a una estructura de árbol potencialmente grande, y las ramas se recortan ya que solo nos preocupamos por las coincidencias cercanas. Con la recursividad, son quizás veinte líneas de código (tengo las versiones Fortran y C). Creo que sería desordenado de lo contrario. ¡Diablos, fue mucho más fácil programar / depurar la verificación que pensar en ese árbol!

Omega Centauri
fuente
0

Lo entiendo pero nunca lo he usado explícitamente. Creo que cualquiera que se llame a sí mismo programador debe saber o aprender lo que es tanto como necesita saber qué son la pila y el montón. Hola, todo el mundo necesita saber cómo funciona min / max en tic-tac-toe, a través de una recurrencia elegante.

johnny
fuente