Content deleted Content added
deleted sentence containing zero information. |
Betsy Camus (talk | contribs) Tag: references removed |
||
Line 3:
==Examples==
===
El ejemplo más importante de un tipo de dato que se puede definir con una recursión mutua es un árbol, el cual puede ser definido mutua y recursivamente en términos de un conjunto de árboles.
Simbólicamente:
f: <nowiki>[t[1], ..., t[k]]</nowiki>▼
t: v f
Un bosque f consiste en una lista de árboles, mientras un árbol t consiste en un par de valores v y un sub-árbol f. Esta definición es ordenada y fácil para trabajar abstractamente (como cuando se prueban teoremas sobre las propiedades de los árboles), tal como se expresa un árbol en términos simples: una lista de un tipo y un par de dos tipos. Además, une muchos algoritmos de árboles, que consisten en hacer una operación con el valor, y otra cosa con los sub-árboles.
Esta definición mutuamente recursiva se puede convertir a una definición recursiva individual al delimitar la definición de conjunto de árboles:
t: v <nowiki>[t[1], ..., t[k]]</nowiki>▼
Un árbol t consiste en un par de un valor v y una lista de árboles (sus hijos). Esta definición es más compacta, pero algo más desordenada: un árbol consiste en un par de un tipo y una lista de otro, que requieren desenmarañarse para probar los resultados.
En Standar ML, los tipos de datos de árbol y bosque se pueden definir mutual y recursivamente de la siguiente manera, lo que permite árboles vacíos:
datatype 'a tree = Empty | Node of 'a * 'a forest▼
and 'a forest = Nil | Cons of 'a tree * 'a forest ▼
▲datatype 'a tree = Empty | Node of 'a * 'a forest
▲and 'a forest = Nil | Cons of 'a tree * 'a forest
===Computer functions===
Del mismo modo que los algoritmos en los tipos de datos recursivos pueden ser dados naturalmente por funciones recursivas, los algoritmos en las estructuras de datos mutuamente recursivas pueden ser dados naturalmente por funciones mutuamente recursivas. Los ejemplos comunes incluyen algoritmos en árboles y analizadores de descenso recursivos. Al igual que con la recursión directa, la optimización de la cola de cola es necesaria si la profundidad de recursión es grande o ilimitada, como el uso de la recursión mutua para la multitarea. Tenga en cuenta que la optimización de llamadas finales en general (cuando la función llamada no es la misma que la función original, como en las llamadas recursivas de cola) puede ser más difícil de implementar que el caso especial de optimización de llamadas recursiva de cola, y por lo tanto una implementación eficiente de La recursión de cola mutua puede estar ausente de los lenguajes que solo optimizan las llamadas recursivas de cola. En idiomas como Pascal que requieren declaración antes del uso, las funciones mutuamente recursivas requieren la declaración directa, ya que una referencia directa no se puede evitar al definirlas.
Al igual que con las funciones recursivas directas, una función contenedora puede ser útil, con las funciones mutuamente recursivas definidas como funciones anidadas dentro de su alcance si esto es compatible. Esto es particularmente útil para compartir estados a través de un conjunto de funciones sin tener que pasar parámetros entre ellos.
====Ejemplos básicos====
Un ejemplo estándar de recursión mutua, el cual es ciertamente artificial, Determinar si un número no negativo es par o impar por definición dos funciones separadas que se llaman una a la otra, disminuyendo cada vez.
bool is_even(unsigned int n) {
if (n == 0)
Line 41 ⟶ 43:
return is_even(n - 1);
}
¿Esas funciones son basadas en la observación de la pregunta 4 es par? Lo cual es equivalente a ¿3 es impar? El cual a su vez equivale a preguntarnos si ¿2 es par?, y así sucesivamente hasta llegar a cero. Este ejemplo es una solo una mutual recursión, y podría ser fácilmente por interacción. En este ejemplo, la llamada mutuamente recursiva son colas de llamadas, las cuales para optimizarlas sería necesario ejecutar un espacio de pilas constante. In C, esto tomaría O(n) de espacio de pilas, a menos que se reescriba la función usando saltos en vez de llamados. Esto podría se reducido por solo una función recursiva is_even. En ese caso, is_odd,el cual podría ser alineado, llamaría is_even, pero is_even solo se llamaría a sí misma.
Una clase de ejemplo más general, un algoritmo de un árbol puede ser descompuesto dentro de su comportamiento en un valor y el comportamiento de los sub-arboles(children), y puede ser dividido dentro de dos funciones mutuamente recursivas. Una especificando el comportamiento en un árbol, llamando a la función forest para el conjunto de sub-arboles, y otra especificando el comportamiento de un conjunto de árboles, llamando a la función tree para el árbol del conjunto de árboles. En Python:
▲ def f_tree(tree):
f_value(tree.value)
f_forest(tree.children)
Line 55 ⟶ 53:
for tree in forest:
f_tree(tree)
En este caso la función tree llama a la función forest por solo una recursión, mientras que la función forest llama a la función tree por recursión múltiple.
Usando el tipo de datos Standard ML en el ejemplo anterior, el tamaño de un árbol (número de nodos) puede ser calculada por medio de las siguientes funciones recursivas:
fun size_tree Empty = 0
| size_tree (Node (_, f)) = 1 + size_forest f
and size_forest Nil = 0
| size_forest (Cons (t, f')) = size_tree t + size_forest f'
Un ejemplo mas detallado en Scheme, contando lo dejado de una árbol:
(define (count-leaves tree)
(if (leaf? tree)
Line 78 ⟶ 75:
(+ (count-leaves (car forest))
(count-leaves-in-forest (cdr forest)))))
Estos ejemplos reducen fácilmente a una sola función recursiva mediante la adjunción de la función forest en la función tree, el cual es comúnmente puesto en práctica: las funciones directamente recursivas que operan en procesos secuenciales de árboles el valor del nodo y recursión en los sub-arboles dentro de una función en lugar de dividirlas dentro de dos funciones separadas.
====
Un ejemplo más complicado es dado por un analizador descendente recursivo, el cual puede ser naturalmente implementado por una función por cada regla de producción de una gramática; eso en general será una recursión múltiple, como las reglas de producción generalmente combina múltiples partes. Eso puede ser hecho sin recursión múltiple, por ejemplo, aun teniendo aun teniendo funciones separadas para cada regla de producción, pero teniéndolos llamados por una sola función controladora, o poniendo toda la gramática en una sola función.
Recursión mutua puede También implementar una maquina finita de estado, con una función por cada estado, y solo una recursión en cada cambio de estado; eso requiere colas de llamados de optimización, si el número de estados es largo o abundante. Eso puede ser usado como una simple cooperación de multitareas. Un enfoque similar de multitareas es por ejemplo usar cortinas, el cual llama a cada otro, donde en lugar de llamar a otra rutina cede a otro, pero no lo termina, y luego pasara a la ejecución cuando es cedido de regreso. Eso permite cortinas individuales para mantener el estado, sin su necesidad de pasar por otros parámetros en variables compartidas.
Existen algunos algoritmos los cuales naturalmente tienes dos fases, como MIN MAX (min y máx.), y esos pueden ser implementados teniendo cada fase en una función separadas con recursión mutua, a pesar de que ellos pueden También ser combinados dentro de una sola función con recursión directa.
===Funciones matemáticas===
En matemáticas, las secuencias [[Hofstadter Female and Male sequences]] son un ejemplo de un par de secuencias de enteros definidas de una forma mutuamente recursiva.
Los fractales pueden ser computarizados mediante funciones recursivas: Esto, en algunos casos, puede resultar más ordenado usando funciones mutuamente recursivas; la [[Sierpiński curve]] es un ejemplo.
===
La recursión mutua es muy común en el ámbito de programación funcional y es regularmente usado en programas desarrollados in LISP, Scheme, ML y lenguajes similares. En lenguajes como Prolog, el uso de la recursión mutua es casi inevitable.
Algunos estilos de programación no incentivan el uso de la recursión mutua, aludiendo a que podría resultar confuso distinguir entre las condiciones en las cuales el programa retorna un resultado de las cuales generarían que el programa nunca termine su ejecución y no genere un resultado. Peter Norving señala a un patrón de diseño que desalienta su uso por completo, indicando:
Si tienes dos funciones mutuamente recursivas que alteran el estado de un objeto, intenta establecer todas las instrucciones en solo una de las funciones. De otro modo probablemente termines duplicando tu código.
===Terminología===
La recursión mutua es también conocida como recursión indirecta, en contraste con la recursión directa donde una función se llama a sí misma directamente. Esto solo es una diferencia de énfasis, no diferentes nociones: la recursión indirecta se refiere a una sola función, mientras la recursión mutua se refiere a un conjunto de funciones y no a una función individual. Por ejemplo, si f se llama a sí misma, eso es la recursión directa. En cambio, si f llama a g y luego g llama a f, que vuelve a llamar a g, desde el punto de la función f, f es indirectamente recursiva, desde el punto de g, esta también es indirectamente recursiva, mientras que desde el punto de f y g, son mutualmente recursivas una en la otra. Análogamente en un conjunto de tres o más funciones que se llaman una a la otra pueden ser denominadas un conjunto de funciones mutualmente recursivas.
==Prevalence==
|