¿A los programadores les gustan las funciones recursivas? ¿Por qué o por qué no?

Mi propia perspectiva es que los profesores de CS y los escritores de artículos en “revistas aprendidas” de informática profesional parecen preferir la recursión mucho más que los programadores que trabajan y aquellos que crean y mantienen sistemas de código reales, serios (a gran escala).

La recursión es breve e inteligente, pero como cuestión práctica, cada iteración (cada vez que una rutina recursiva se llama a sí misma) carga la pila de llamadas de retorno con un montón de datos de contexto. En algún momento, a medida que regresan todas estas iteraciones de llamadas recursivas, la pila tiene que ser “desenrollada”. Un resultado práctico es que la recursividad tiende a ser más intensiva en memoria y más lenta que un equivalente, explícito, no conciso, no- iteración inteligente Los codificadores reales tienden a odiar el código lento que acapara la memoria más de lo que les gusta parecer breves e inteligentes.

Existe una prueba matemática bien conocida de la proposición de que cualquier recursión se puede replantear como una iteración lógicamente equivalente.

A veces, cuando ya he escrito una subrutina y me doy cuenta de que tenía que ser un bucle, y miro el problema más amplio y me doy cuenta de que la iteración que debería haber escrito solo se repetirá una o dos veces o un pequeño número de veces, luego Hago una rápida “recursión de cola” fuera de la subrutina, y sigo adelante. No estoy orgulloso de esto, pero a veces ha sucedido (muy raramente). De lo contrario, nunca me molesto en pensar en la recursión.

Me atrevo a decir que lo mismo es cierto para muchos, muchos otros codificadores que realmente se han ganado la vida produciendo código. Quizás incluso la mayoría de esos codificadores son como yo a este respecto.

Me gustan las definiciones recursivas cuando trato con problemas que se ajustan al razonamiento inductivo. El diseño del lenguaje a menudo implica mucha recursividad, no solo en la implementación, sino también en las propiedades de prueba del lenguaje.

No me gustan las definiciones recursivas cuando no lo hace. Abusar de las llamadas de cola para escribir un bucle de evento oculta recursivamente la intención. Incluso la co-recursión no es exactamente lo mejor.

En cuanto a las implementaciones, no me gusta usar la recursividad cuando el lenguaje no tiene un buen soporte para ello. “Tener un buen soporte” aquí no significa “puede escribir funciones recursivas”, sino más bien “la implementación optimiza la recursividad, de modo que no tenga que preocuparse por los desbordamientos de la sobrecarga y la pila”. Obviamente, la mejor manera de resolver esto es no tener una pila para llamadas a funciones en primer lugar, como lo hace Haskell. Sin embargo, no todos los idiomas pueden hacer eso …

Un buen ejemplo de los problemas que puede encontrar al usar idiomas sin un buen soporte para la recursividad es implementar la Impresora Prettier de Philip Wadler [1]. La idea es construir un árbol de posibilidades para el diseño y formatearlo comprobando cuánto espacio de la pantalla queda para adaptarse al elemento actual. Es muy sencillo escribirlo como un algoritmo estructuralmente recursivo, que es como Wadler lo define en el documento.

Esa implementación funciona sorprendentemente bien en Haskell.

Si intenta implementarlo en un lenguaje que no garantice una cantidad constante de espacio para la recursividad, sin embargo, le garantizamos un desbordamiento de pila. Y para corregir que tiene que traducir el algoritmo para que sea iterativo (no es fácil) o traducirlo para que sea recursivo en la cola y trampolín su implementación (no es bonita, y ciertamente no es eficiente).

Por cierto, esto sucede para casi todo en compiladores e intérpretes. Siempre puede traducir los documentos y algoritmos para que sean iterativos, pero generalmente no es tan fácil o fácil de mantener.

Muy pocos lenguajes de programación tienen un buen soporte para la recursividad, y la mayoría de esos lenguajes no son convencionales de ninguna manera, por lo que es comprensible que a la mayoría de los programadores no les guste usarlo fuera de esos, incluso cuando la recursión es la mejor herramienta para lo que son. resolviendo, en teoría.

Notas al pie

[1] https://homepages.inf.ed.ac.uk/w

Vivo y respiro recursión. La mayor parte de mi trabajo es sobre analizadores matemáticos y algoritmos geométricos para construir mallas de formas 3D definidas por ecuaciones implícitas. Ambos dominios conducen naturalmente a algoritmos recursivos.

Con un análisis generalmente está trabajando con un árbol de sintaxis abstracta. El tipo natural de código para lidiar con esto es la recursividad. Minimice la cantidad de información que necesita ver, así que solo mire un nodo, a la vez. Facilita la depuración del código.

Otras respuestas se han quejado de problemas con desbordamientos de pila. En el analizador matemático que vendemos comercialmente (Jep Java – Math Expression Parser – Singular Systems) solo hemos tenido un problema con desbordamientos de pila en el análisis. Realmente necesita llegar a extremos bastante largos para producir una expresión que cause problemas, algo así como un árbol de 1000 nodos de profundidad. A ningún humano se le ocurrirá una expresión que y el único problema que encontramos fue con las expresiones generadas por máquina.

Así que he escrito código para trabajar con estas expresiones extremas, el código es más complejo. Diría que tratar de evitar la recurrencia cuando es la solución natural es similar a “la optimización prematura es la raíz de todo mal”.

Es altamente dependiente del contexto.

Cualquier cosa que se pueda expresar como una función recursiva también se puede expresar como un bucle, y viceversa. Por ejemplo, la suma de una secuencia se puede escribir como un bucle sobre todos los elementos de la secuencia, agregando cada uno de ellos a una variable que luego se devuelve; o como el último elemento de la secuencia más la suma de todos, excepto el último.

Elegir el correcto depende del lenguaje de programación utilizado, el algoritmo específico que se implementa y la cantidad esperada de datos con los que operará el programa. En un lenguaje como, por ejemplo, Python, deberá tener cuidado al recurrir ya que no hay una optimización de llamada de cola. Cada nivel de recursión usará memoria, y eventualmente se encontrará con un límite de profundidad (generalmente menos de mil). C y C ++ no tienen un límite fijo, pero eventualmente puede quedarse sin espacio de pila y bloquearse, a menos que el compilador haya logrado optimizar la recursión. Esto puede suceder incluso en un lenguaje como Haskell, que está diseñado para ser recursivo.

Sin embargo, recurrir puede tener sentido incluso en Python. Por ejemplo, si sabe que nunca irá más allá de un número fijo (bajo) de niveles profundos, entonces no necesita preocuparse por el límite de recurrencia. Algunos algoritmos son simplemente más fáciles de expresar como recursivos. Por ejemplo, construir una estructura de aceleración espacial como un octree es más fácil de escribir con recursividad incluso en lenguajes donde los bucles son el enfoque generalmente preferido.

Las cosas se vuelven aún más complejas por el hecho de que algunos algoritmos que son realmente recursivos en la naturaleza, como el renderizado con trazado de rayos fotorrealistas, en realidad a menudo son más eficaces cuando se expresan como un bucle.

TL; DR: Depende.

No es posible hablar sobre funciones recursivas en una talla única para todos. La configuración sobre la que habla sobre las funciones recursivas cambia la discusión considerablemente.

En primer lugar, si son lenguajes de procedimiento, como C / C ++, generalmente no pueden razonar tan bien sobre el código, debido a la “apertura” del lenguaje, no a la “restricción”. Esto significa que el compilador simplemente no puede hacer ciertas suposiciones de que el programador no hizo algo “algo loco”. En otras palabras, no puede modificar la recursión en un bucle simple como lo hacen algunos lenguajes de programación funcionales. A veces, más restricciones son algo bueno.

Eso significa que la pila del programa está consumiendo toneladas de espacio cada vez que se repite la función. Normalmente, una pila asignada manualmente en el montón permitirá reescribir el código en un bucle, incluso si es una tarea jerárquica.

Sin embargo, eso me lleva al siguiente punto. La recursión es casi una bendición para las tareas jerárquicas o anidadas, como enumerar todos los archivos en un directorio y todos los subdirectorios. La recursión y los árboles van juntos como la mantequilla de maní y los plátanos.

Entonces, la cuestión es que si está trabajando con un lenguaje de procedimiento (o más específicamente un lenguaje que NO APOYA la “eliminación de la cola”), prefiera un bucle sobre la recursividad. Si no puede reescribirse usando un bucle, use la recursión, pero la mayoría de las cosas se pueden reescribir en un bucle si crea una pila en el montón (llamemos a eso “recursión falsa” para esta discusión).

Además, hay algunos algoritmos que son bastante mejores que otros, que solo pueden ocurrir con cierta recurrencia (ya sea “falso” o “verdadero”). Elevar un número a una potencia entera se hace mejor con recurrencia de algún tipo.


Como anécdota, quería hablar sobre mi experiencia con un reclutador de Microsoft que me hizo esta pregunta directamente, “¿Cuál es mejor, iteración o recursión?” Básicamente dije lo mismo que dije aquí, donde se prefiere la iteración, pero hay casos en los que la recursión pateará las iteraciones hacia atrás cualquier día. Le expliqué la situación de exponenciación de enteros, y él básicamente me acusó de estar dormido en mis estudios y de no poder entender cómo funcionan las computadoras, ya que no entendí cómo funciona la pila de programas.

Curiosamente, en F #, creado por Microsoft, tienen la función ‘pown’ que hace exactamente lo que dije aquí.

El punto de esta discusión es, cuando preguntas “¿A los programadores les gusta X?” Es mejor que entiendas que los programadores tienden a las “guerras religiosas” en las que ciertas ideas se anulan solo por principio. No siempre es posible obtener la respuesta correcta, a menos que pueda obtener a alguien que haya hecho los cálculos, realice una prueba de rendimiento y la haya utilizado en su trabajo. De lo contrario, preguntas como esta provocarán fuertes respuestas de personas que nunca han hecho el trabajo.

No culpo a Microsoft por ese reclutador tonto, pero menciono esto porque es una buena lección en la cultura de los ingenieros y programadores de software, especialmente los jóvenes, que muchas veces no mirarán el panorama general.

Me incluyo en esa lista.

Varía de programador a programador. Personalmente, me ocupo de una gran cantidad de computación científica que implica un gran número de cálculos. Por lo tanto, es preferible evitar la recurrencia a menos que sea una llamada de cola. De hecho, recientemente escribí un artículo de investigación que apunta a reemplazar una técnica recursiva de divide y vencerás por un método de análisis de series de tiempo específico por un nuevo algoritmo no recursivo desarrollado por mí. El método recursivo puede consumir de 5 a 20 veces más memoria que el iterativo. El acceso repetido a los marcos de la pila también puede degradar la velocidad de ejecución.

Hay algunos aspectos que vale la pena tener en cuenta al codificar. Tu código debe ser

  1. Correcto
  2. Eficiente
  3. Elegante
  4. Fácil de desarrollar y mantener.

El código recursivo es extremadamente elegante y, a menudo, fácil de escribir y verificar la corrección.

El equivalente iterativo de una función recursiva (especialmente, cuando hay más de una rama en cada instancia) puede no siempre ser fácil de codificar correctamente y es menos lindo. Sin embargo, es mucho más eficiente que su contraparte recursiva.

Por lo tanto, se reduce a la facilidad de desarrollo / elegancia versus eficiencia. Si puedo escapar de la sobrecarga de memoria y rendimiento de las funciones recursivas, las preferiría. Si realmente necesito ahorrar memoria y tiempo de ejecución a costa del tiempo de desarrollo / elegancia, voy iterativo.

Como programador, tengo que tener un algoritmo incluso para elegir metodologías :—)

Las funciones recursivas en general son más concisas, por lo que de alguna manera también pueden ser más fáciles de leer. Algunos lenguajes, como los lenguajes que admiten programación funcional como Scheme o Haskell, alientan el uso de la recursión en lugar de las declaraciones en bucle porque está más cerca de las declaraciones matemáticas. Entonces diría que a los programadores que crean programas que usan la Programación Funcional en general les gusta escribir funciones recursivas sí (me gusta).

Sin embargo, por otro lado, en lenguajes imperativos como Python o Java , ni siquiera tiene Tail Call Optimization (TCO). Por lo tanto, este no es un buen lugar para escribir funciones de recursión, ya que tenemos un número limitado de llamadas de pila, por lo que crear un proceso de recursión realmente profundo puede crear la famosa excepción StackOverflow . En Python, por ejemplo, el número de llamadas recursivas anidadas limitadas es 1000. Por lo tanto, recomendaría evitar escribir funciones recursivas en estos lenguajes y crear una versión iterativa.

Permítanme decir que las funciones recursivas de llamada de cola pueden crear procedimientos iterativos si están optimizados para cortar la última operación de pila en la llamada recursiva. Escribí anteriormente una respuesta relativamente buena sobre esto en StackOverflow en el contexto de Lisp : ¿Por qué una función que se llama a sí misma en su cuerpo no se considera recursiva (versus iterativa)?

Mi respuesta final es: si su idioma tiene TCO por algunos problemas, la creación de funciones recursivas de cola es agradable.

Pero tenga esto en cuenta: las funciones recursivas que no son recursivas de llamada de cola son más caras en la complejidad del espacio, por lo que pueden ser más ineficientes que la versión iterativa.

Si estás hablando de la recursividad de pila, lo odio. Significa que alguien era demasiado vago para acoplar un bucle con una variable / contenedor de estado apropiado y, en cambio, está utilizando un mecanismo diseñado para recordar dónde estaba cuando llamaba a una función. En lugar de simplemente colocar el estado necesario en la pila, está empujando un montón de otra información que no necesita guardarse.

Además, la recursividad de la pila significa que corre el riesgo de un desbordamiento de la pila. Eso puede ser un agujero de seguridad o simplemente una excepción molesta, dependiendo de la situación.

A la gente le gusta usar la recursividad de pila para analizar el texto de entrada, como JSON. Pero todo lo que un hacker debe hacer es escribir una expresión JSON con 100 sub-objetos anidados y vacíos para bloquear el código en el servidor.

Me encantan las herramientas que se ajustan al problema que estoy tratando de resolver.

A veces, las funciones recursivas se ajustan al problema que estoy tratando de resolver perfectamente.

Por ejemplo, aquí hay una función que escribí para un proyecto reciente.

proceso vacío estáticoDir (dir de cadena)
{
foreach (subDir de cadena en Directory.GetDirectories (dir))
{
processDir (subDir);
}
foreach (archivo de cadena en Directory.GetFiles (dir))
{
processFile (archivo);
}
}

Necesitaba revisar una estructura de directorios y hacer cambios en todos los archivos, independientemente de cuán profundamente estuvieran anidados dentro de las carpetas.

Sin recurrencia, habría sido una función increíblemente desordenada para escribir.

La recursión no se ajusta a todos los escenarios, pero en los escenarios donde encaja, funciona muy bien. Lo curioso es que la recursión parece ser algo difícil para muchos novatos, pero creo que lo hacen demasiado complicado.

Cualquiera puede entender la lógica simple de: “Abra un directorio y lea todos los archivos en ese directorio. Si hay subdirectorios, repita para cada uno “.

Eso es todo recursión es. Claro, la lógica puede ser más complicada para problemas más complicados, pero la recurrencia en sí misma no se vuelve más complicada que la simple idea de “repetir estos pasos para esta otra situación similar”.

Aquí hay cosas que los programadores generalmente toman en consideración:

  • Mantenibilidad Cambio de requisitos. La arquitectura tiene que responder al cambio.
  • Legibilidad humana. Incluso si solo un programador trabaja en un proyecto, en unos pocos meses podría ser un programador diferente.
  • Actuación. El programa debe hacer que la computadora haga lo menos posible.

Las funciones recursivas suelen fallar en las dos últimas cuentas. Si la recursividad es mutua (donde dos funciones llaman a la otra), a menudo también puede fallar en la primera. Aumentar el número de iteraciones en un orden de magnitud puede funcionar bien con un bucle, pero puede causar un desbordamiento de la pila con recursividad.

Hay momentos en que la recursión se siente como la forma intuitiva de resolver un problema. La búsqueda binaria es un ejemplo de esto. Sin embargo, para alguien que mira el código por primera vez es difícil razonar en comparación con un ciclo regular. Requiere que el programador cree una pila mental encima de la pila mental que ya tenemos.

Para algunos casos, como la implementación del análisis de idiomas, reduce en gran medida el código redundante y tiene sentido lógico.

Pero para la mayoría de los casos, es innecesario. Crea más complejidad mental y empeora el rendimiento. Para muchos idiomas no está optimizado.

Creo que escribir un compilador e intérprete de idiomas demostrará lo malo y lo bueno de la recursividad. Lo más probable es que tenga que usarlo, pero la implementación de funciones y ámbitos mostrará lo que está mal.

No estereotipando a todos los programadores aquí, pero personalmente me encantan las funciones recursivas.

¿Por qué? Bien, hagamos un episodio final corto y feliz de Doraemon para explicar que:

Recuerde ese episodio de Doraemon, cuando Nobita le pide que complete toda su tarea antes de la mañana (que era casi imposible).

Vamos a cambiar la trama desde aquí: –

¿Qué hizo Doraemon?

Tomó su máquina del tiempo, fue a diferentes Doraemon de diferentes períodos de tiempo y le pidió que lo ayudara a completar el trabajo. Pero el otro Doraemon sabía que el Doraemon original hizo trampa y mantuvo el trabajo que puede hacer fácilmente en una noche para sí mismo y le dio el resto y es imposible completar el trabajo que tiene, en una noche.

¿Ahora qué debería hacer? Hizo lo mismo que el Doraemon original. Tomó su máquina del tiempo y pasó el trabajo a otro Doraemon, quedándose una noche trabajando para él y pasando el resto.

Doraemons continuó esto hasta que el trabajo que se pasó, se redujo lo suficiente, para completarse en una noche.

Ahora el último Doraemon, completó su trabajo en una noche y le devolvió el trabajo al Doraemon que le dio el trabajo.

Esto continuó hasta que el trabajo llegó al Doraemon original y luego le pasó el trabajo terminado a Nobita, que felizmente puede ir a asistir a su clase ahora 🙂


Aquí somos Nobita y el Doraemon original es nuestra función recursiva.

Lo que tenemos que hacer es simplemente darle el trabajo y dormir (no literalmente: p) y descansar. Él se encargará de eso.

Y es por eso que amo a Doraemon (funciones recursivas) 😀

PD: – El enfoque no recursivo de Doraemon podría haber sido hacer clones de sí mismo y dar a cada uno de ellos la cantidad de trabajo que pueden completar en una noche.

No hay problema en eso. Y sin mencionar que es una gran idea. Pero eso habría creado un desastre en la casa de Nobita. Y sabes cómo habría reaccionado su madre a eso 🙂

Moraleja de la historia: – Si puede hacer lo que necesite, por método no recursivo, sin crear ningún desorden, continúe con cualquier método. Pero si su enfoque de fuerza bruta / no recursivo está creando un desastre y sabe que puede implementarlo a través de la recursión casi sin problemas, ¿por qué no usarlo?

PPS: – A veces tienes que cambiar un poco la historia para que sea moralmente correcta.

Gracias por leer 🙂

Agente-J

Me gusta la recursividad donde la intención del código es clara (clasificación rápida), la cantidad de memoria requerida puede determinarse dentro de límites razonables y la condición de terminación está asegurada.

La recursión también puede ser extremadamente útil en SQL, especialmente cuando se trata de datos jerárquicos, como los que se pueden encontrar en listas de materiales o en el árbol genealógico.

El lenguaje de programación Erlang presentó algunos desafíos interesantes para comprender cómo se puede usar la recursión y servir como una introducción única y algo desalentadora a la recursividad de cola.

Me complace usarlo cuando es apropiado, pero en realidad no es apropiado con tanta frecuencia.

La recursión tiene una reputación de ser difícil de entender porque no es familiar, y es familiar porque no muchas cosas en el mundo realmente exhiben una estructura recursiva. Incluso cuando lo hacen, las personas tienden a pensar en ellos solo en términos de un conjunto finito y pequeño de niveles. O terminan pensando en los estados terminales como un conjunto, y no se preocupan demasiado por la navegación.

Intento lo mejor que puedo para que mi código imite la estructura real de las cosas. Si el mundo real no es recursivo, mi código generalmente no tiene que serlo.

Las cosas informáticas a menudo tienen una estructura recursiva. La página web que está viendo es un ejemplo: es un gran elemento web, lleno de elementos web más pequeños, lleno de elementos web aún más pequeños, y así sucesivamente. También lo son los lenguajes de programación que usa para escribirlo: un programa es una colección de declaraciones, y en la declaración if está hecha de dos sub-declaraciones, que pueden tener sub-sub-declaraciones, etc.

Pero rara vez tengo que trabajar con ese tipo de cosas de forma recursiva. A menudo, alguien más lo ha hecho por mí, y todo lo que necesito hacer es adjuntarme a pasos específicos en la recursión. Si quiero subrayar cada párrafo de esta página que contiene mi nombre, sería más fácil decir “Busca los párrafos, y para cada uno, si tiene mi nombre, hazlo subrayado”. Hay una recurrencia en el “ir a buscar”, pero alguien más lo escribió.

De manera similar, podría estar representado por un objeto como {name: "Joshua", quoraData: {numberOfAnswers: 100, votes: {upvotes: 334, downvotes: 11}}…} , pero rara vez necesito tratar con la estructura en abstracto . Es mucho más probable que pida josh.quoraData.votes.upvotes .

Puedo escribir algo recursivo, digamos, una vez al mes más o menos, tal vez menos. Y cuando lo hago, es divertido. Creo que si no obtienes recursividad, realmente no entiendes qué es una llamada a función. Una vez que tienes eso, en realidad no es tan difícil. Pero todavía está bien que funcione.

En principio / como concepto, me encanta la recursividad. Es como una prueba inductiva en matemáticas: si sabe cómo manejar todos los casos realmente simples y sabe cómo dividir cualquier caso complicado en un conjunto de casos menos complicados, ya está . Como un ejemplo perfecto de esto, considere el pseudocódigo-que-también-pasa-a-ser-válido-Python:

def fibonacci (n):
si n <= 2:
volver 1
retorno fibonacci (n-1) + fibonacci (n-2)

Corto, elegante, sencillo de escribir, fácil de leer / comprender …

… y terriblemente ineficiente. No solo tiene sobrecarga de llamadas de función de una cantidad potencialmente enorme de llamadas (consumiendo tanto tiempo como espacio de pila, como otros han discutido), ¡también está calculando la misma llamada de función más de una vez! De hecho, el número de llamadas a funciones crece exponencialmente con [math] n [/ math], mientras que el método estándar de papel y lápiz toma solo un número lineal de pasos.

Un compilador optimizador puede deshacerse de ese primer problema (poner los ojos en blanco y traducir sus llamadas recursivas en un bucle), dependiendo del idioma / plataforma para el que esté escribiendo, pero no estoy al tanto personalmente de ningún eso arreglará el segundo para ti. Por supuesto, puede arreglarlo usted mismo con la memorización, pero ahora ha perdido la elegante simplicidad que hizo que esta solución fuera tan atractiva en primer lugar. En ese punto, también puedes escribir:

def fibonacci (n):
prev = 1
cur = 1
para i en rango (n-2):
nxt = prev + cur
prev = cur
cur = nxt
volver cur

o similar. Se necesita un poco más de escrutinio para verificar que hace lo que desea, pero en última instancia sigue siendo bastante fácil de escribir y leer, y mucho más eficiente * en todas las formas concebibles, ya sea que tenga un compilador inteligente o no.

Entonces, en la práctica , solo uso funciones recursivas muy ocasionalmente. Más a menudo, redacto algo como una función recursiva para desarrollar la estructura / organizar todo y luego traducir el código a una forma menos derrochadora. Para mí, al menos, eso generalmente combina lo mejor de ambos mundos: puedo pensar en términos de una estructura limpia y recursiva, y solo una vez que he resuelto el problema subyacente , necesito comenzar a preocuparme por implementar el algoritmo ” en su lugar “en un solo marco de pila.


[*]: También es posible calcular fibonacci (n) mucho más rápido (a menos que [math] n [/ math] sea pequeño), usando la expresión de forma cerrada, pero eso no es ni aquí ni allá. También implica operaciones de punto flotante, que pueden volverse inciertas para [math] n [/ math] suficientemente grande, ya que el error resultante puede ser mayor que uno.

Aunque, considerando el análisis de la complejidad del tiempo, no es tan eficiente, me gusta por su forma natural. La recursión como método es muy intuitiva y ahorra tiempo para comprender un tema complejo. Te doy un ejemplo de ‘hola mundo’:

Este pequeño código en Python puede calcular n th Fibonacci y se ve tan hermoso e intuitivo.

Mi elección personal generalmente son las funciones no recursivas, ya que generalmente pueden ser mucho más rápidas que las funciones recursivas. Siempre apunto por la velocidad sobre la elegancia, ya que creo que cualquier problema que estés resolviendo, siempre puede crecer hasta el punto en que se desacelera a una velocidad inutilizable. Por lo tanto, la velocidad máxima te dará más espacio para la cabeza.

Sin embargo, cuando la velocidad absoluta es menos crítica y el problema se presta a funciones recursivas, por ejemplo, análisis de árbol, consideraré el uso de la misma.

Las funciones recursivas tienden a consumir más memoria en comparación con el uso de bucles. Si bien la mayoría de los algoritmos implementados usando recursión también se pueden implementar usando bucles, todavía hay un caso excepcional (la función de ackermann, también conocida como doble recursión). Por lo tanto, las personas prefieren usar la recursividad solo cuando es necesario a pesar de que se ve más fresco.

Personalmente estoy muy amargado por la recursividad.

Es difícil colocar en sistemas complejos, donde necesita todo tipo de casos secundarios, puede hacer que su pila explote si el problema es lo suficientemente grande, y cuando lo empuja por el rendimiento (por ejemplo, con programación dinámica) pierde mucho Su atractivo inicial.

Siento que a la gente le gusta la recursión, principalmente debido a un deseo de “elegancia” del TOC, que para mí es una bandera que debería ir en la otra dirección.

Déjame ponerlo de esta manera. Siento que a las personas que les gusta la recursión también les gusta un escritorio limpio y vacío, y ustedes conocen la famosa frase: “Si un escritorio desordenado es un signo de una mente desordenada, ¿de qué, entonces, es un escritorio vacío un signo?”.

Yo diría que los programadores experimentados lo hacen. Hay algunos problemas, como atravesar árboles, que se manejan elegantemente con su uso. Digo “experimentado” porque es un poco difícil entender cómo usarlos de manera efectiva a menos que primero tengas algunas habilidades básicas de programación.

Un ejemplo de lo que no se debe hacer es usarlos para calcular un factorial (N!). Puedes hacerlo recursivamente, pero es ineficiente. Este es uno de los primeros ejemplos que se presentan en una discusión de “cómo hacerlo”. Genial para la teoría, pero malo en la práctica.

More Interesting

Se le da una matriz de números MxN, con la propiedad de que los números aumentan a medida que avanza por cada columna y hacia la derecha en cada fila. ¿Cómo puede verificar eficientemente si un número dado está en la matriz?

¿Qué estructuras de datos / algoritmos de coincidencia usa vimdiff?

¿Cuál es la forma más fácil de animar el algoritmo de Dijkstra para Power Point Presentation?

¿Qué representa un estado en términos de programación dinámica?

¿Cuál es una explicación simple de cómo ALS completa los valores donde antes existían ceros?

Cómo elegir el mejor algoritmo de aprendizaje profundo o paquete R para un conjunto de datos

¿Es posible heapify un árbol binario a un montón sin usar array?

¿Qué es el WordNet? ¿Cuál es la relación entre WordNet y el algoritmo Leacock & Chodorow?

¿Cómo funciona el algoritmo de acortador de URL?

Trabajo muy duro para estudiar 13 horas al día durante más de 7 meses, pero todavía no puedo mejorar mi estructura de datos y habilidades de algoritmos, ¿qué debo hacer?

Soy nuevo en Quora y no entiendo si las preguntas de coeficiente intelectual son una tendencia constante o si estoy atrapado en alguna forma de algoritmo infernal. Si es así, ¿cómo escapo?

Cómo demostrar que O (f (n) - g (n)) no es necesariamente igual a O (f (n)) - O (g (n))

¿Cuál es el concepto de la función recursiva en matemáticas?

¿Cuál es la conclusión del algoritmo de Dijkstra?

Cómo revertir una lista vinculada usando la recursividad de cola y dos punteros