Convertir una función para usar recursividad de cola: un estudio formal

¿Alguien ha escrito un documento formal que describa un método para (automáticamente) convertir funciones para que sean recursivas de cola? Estoy buscando un tratamiento formal de nivel universitario que incluya las limitaciones (tipos de funciones que se pueden convertir), los procedimientos de conversión y, si es posible, las pruebas de corrección. Los ejemplos en Haskell serían una ventaja.

preguntado el 22 de mayo de 12 a las 12:05

Busqué en Google, pero no pude encontrar ninguna referencia específica. Esperaba que alguien pudiera proporcionar una referencia o dos. -

Una transformación de CPS ciertamente haría el trabajo en el caso más genérico (y algunas optimizaciones consiguientes podrían eliminar la mayor parte de la basura resultante). Se publican toneladas de artículos sobre este tema. -

2 Respuestas

¿un método para (automáticamente) convertir funciones para que sean recursivas de cola?

Así que hay dos partes en esto:

  • reconocer que una función recursiva dada se puede convertir en una forma recursiva de cola y hacer esa transformación
  • implementar llamadas de cola de manera eficiente, por lo que la transformación vale la pena.

Transformación de la recursividad en recursividad de cola

Parece relativamente sencillo reconocer definiciones recursivas de cola sintácticamente. Después de todo, 'cola recursiva' solo significa que la llamada aparece sintácticamente en la cola de la expresión.

Por ejemplo, la gente del Esquema describe:

la mera compilación de autollamadas apropiadas como saltos no es suficiente para lograr una recursión de cola completa. En su lugar, dividimos sintácticamente todas las posiciones de subexpresiones en el idioma de origen en dos clases: posición de cola (o reducción) y posición de subproblema. En la expresión simple (si predicate alternativa consecuente), la predicate es una posición de subproblema, mientras que tanto la consecuente como la alternativa están en posiciones de reducción. Esta noción sintáctica se puede extender fácilmente a subexpresiones arbitrariamente anidadas.

Transformar funciones en llamadas de cola

La parte difícil de su pregunta son las optimizaciones para identificar y transformar cálculos recursivos candidatos en recursivos de cola.

Una referencia está en GHC, que utiliza la inserción y una amplia gama de reglas de simplificación para colapsar las llamadas recursivas, hasta que permanece su estructura recursiva de cola subyacente.

Eliminación de llamadas de cola

Una vez que tenga sus funciones en una forma recursiva de cola, le gustaría implementarlas de manera eficiente. Si puede generar un bucle, es un buen comienzo. Si su máquina de destino no lo hace, entonces el eliminación de llamadas de colanecesitará algunos trucos. Para citar a Odersky y Schinz, citados a continuación,

Se han propuesto varias técnicas a lo largo de los años para eliminar llamadas de cola generales (y no solo recursivas), principalmente para compiladores que apuntan a C.

... poner todo el programa en una gran función y simular llamadas a funciones usando saltos directos o declaraciones de cambio dentro de esta función.

... Una técnica popular es usar un trampolín. Un trampolín es una función exterior que llama repetidamente a una función interior. Cada vez que la función interna desea realizar una llamada de cola a otra función, no la llama directamente, sino que simplemente devuelve su identidad (por ejemplo, como un cierre) al trampolín, que luego realiza la llamada por sí mismo. De este modo, se evita el crecimiento ilimitado de la pila, pero inevitablemente se pierde algo de rendimiento, principalmente porque todas las llamadas realizadas por el trampolín son llamadas a funciones estáticamente desconocidas.

Otra técnica es “Cheney on the MTA” de Henry Baker. Con su técnica, el programa primero debe convertirse al estilo de paso de continuación (CPS), por lo tanto, convertir todas las llamadas en llamadas de cola, y luego puede compilarse como de costumbre. En tiempo de ejecución, cuando la pila está a punto de desbordarse, la continuación actual se construye y se devuelve (o se salta) al trampolín que "espera" en la parte inferior de la pila de llamadas.

contestado el 22 de mayo de 12 a las 14:05

Pensé que el OP también estaba buscando una eliminación de la recurrencia de la cola, pero por la forma en que está redactada la pregunta, el OP parece estar buscando lo contrario (o incluso una generalización de lo contrario): cita: "convertir funciones a be cola recursiva [énfasis mío]" - Attila

El OP pregunta sobre la conversión automática de funciones recursivas a la forma recursiva de cola, para beneficiarse de la eliminación de llamadas de cola. Él no está buscando la eliminación de llamadas de cola en sí. - paquet

La pregunta es ambigua. No tiene claro si está buscando la eliminación de llamadas de cola o la optimización de la recursión de cola en general. De cualquier manera, esos documentos son el lugar para comenzar. - Don Stewart

"un método para (automáticamente) convertir funciones para que sean recursivas de cola" me parece bastante inequívoco. El mero hecho de que esté usando el término "recurrencia de cola" implica que él sabe lo que es la eliminación de llamadas de cola, y que pide un tratamiento formal de nivel universitario me sugiere (aunque no de manera concluyente) que no está usando accidentalmente términos incorrectos. no entiende del todo. Puedo estar equivocado. - paquet

He agregado secciones sobre: ​​identificar una expresión con una llamada recursiva en la posición de la cola; Referencias de GHC para desplegar llamadas recursivas para producir una llamada de cola - Don Stewart

Mercury contiene un par de optimizaciones para hacer que las cosas sean automáticamente recursivas a la cola. (Mercurio es un lenguaje de programación de lógica de aplicación de pureza, por lo que habla de predicados en lugar de funciones, pero muchas de las mismas ideas se aplican a los programas de Mercury que a los de Haskell. Una diferencia mucho mayor de que sea lógico en lugar de funcional es que es estricto en lugar de perezoso)

La "introducción del acumulador" genera versiones especializadas de predicados con un parámetro de acumulador adicional para permitir que las operaciones asociativas se muevan antes de la llamada recursiva. Aparentemente, esta optimización no necesariamente da como resultado predicados recursivos de cola por sí solo, pero a menudo da como resultado una forma que puede optimizarse con la segunda optimización:

Los "constructores de módulo de última llamada" esencialmente permiten reescribir una llamada recursiva seguida solo por aplicaciones constructoras de modo que el valor se construya primero conteniendo un "agujero" y luego la llamada recursiva devuelve su salida directamente a la dirección de memoria del "agujero". " en lugar de usar la convención normal de paso de valores devueltos. Sin embargo, creo que Haskell obtendría esta optimización de forma gratuita simplemente por pereza.

Ambas optimizaciones se describen en el artículo Hacer que los programas de Mercury sigan recursivamente.

contestado el 22 de mayo de 12 a las 14:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.