En primer lugar, F # soporta funciones mutuamente recursivas de forma nativa, ya que puede beneficiarse de la instrucción tailcall
que está disponible en .NET IL (MSDN). Sin embargo, esto es un poco complicado y puede no funcionar en algunas implementaciones alternativas de .NET (por ejemplo, Frameworks compactos), por lo que a veces puede necesitar lidiar con esto a mano.
En general, lo que hay un par de maneras de tratar con él:
Trampolín - una excepción cuando el nivel de recursividad es demasiado alto e implementar un bucle de nivel superior que las manijas la excepción (la excepción llevaría información para reanudar la llamada). En lugar de una excepción, también puede devolver un valor que especifique que se debe llamar nuevamente a la función.
desenrolla mediante temporizador - cuando el nivel de recursividad es demasiado alto, se crea un temporizador y darle una devolución de llamada que será llamado por el temporizador después de un tiempo muy corto (el temporizador continuará la recursividad, pero el la pila usada se eliminará).
Lo mismo podría hacerse utilizando una pila global que almacena el trabajo que debe hacerse. En lugar de programar un temporizador, agregaría función a la pila. En el nivel superior, el programa elegiría funciones de la pila y las ejecutaría.
Para dar un ejemplo específico de la primera técnica, en Fa # podría escribir esto:
type Result<´T> =
| Done of ´T
| Call of (unit -> ´T)
let rec factorial acc n =
if n = 0 then Done acc
else Call(fun() -> factorial (acc * n) (n + 1))
Esto puede ser usado para funciones mutuamente recursivas también. El ciclo imperativo simplemente llamaría a la función f
almacenada en Call(f)
hasta que produzca Done
con el resultado final. Creo que esta es probablemente la forma más limpia de implementar esto.
Estoy seguro de que hay otras técnicas sofisticadas para manejar este problema, pero esas son las dos que conozco (y que yo utilicé).
Esa cosa temporizador es algo nuevo para mí) gracias – Bubba88