Parece que he encontrado una forma genérica de convertir cualquier procedimiento recursivo a recursión de cola:
- Defina un subprocedimiento auxiliar con un parámetro adicional "resultado".
- Aplique lo que se aplicaría al valor de retorno del procedimiento a ese parámetro.
- Llame a este procedimiento auxiliar para comenzar. El valor inicial para el parámetro "resultado" es el valor para el punto de salida del proceso recursivo, de modo que el proceso iterativo resultante comienza desde donde el proceso recursivo comienza a reducirse.
Por ejemplo, aquí está el procedimiento recursivo original para convertir ( ejercicio SICP 1.17 ):
(define (fast-multiply a b)
(define (double num)
(* num 2))
(define (half num)
(/ num 2))
(cond ((= b 0) 0)
((even? b) (double (fast-multiply a (half b))))
(else (+ (fast-multiply a (- b 1)) a))))
Aquí está el procedimiento convertido, recursivo de cola ( ejercicio SICP 1.18 ):
(define (fast-multiply a b)
(define (double n)
(* n 2))
(define (half n)
(/ n 2))
(define (multi-iter a b product)
(cond ((= b 0) product)
((even? b) (multi-iter a (half b) (double product)))
(else (multi-iter a (- b 1) (+ product a)))))
(multi-iter a b 0))
¿Alguien puede probar o refutar esto?
algorithms
logic
recursion
lisp
nalzok
fuente
fuente
b
ser una potencia de 2 muestra que inicialmente establecerproduct
0 no es del todo correcto; pero cambiarlo a 1 no funciona cuandob
es extraño. ¿Quizás necesita 2 parámetros de acumulador diferentes?Respuestas:
Su descripción de su algoritmo es realmente demasiado vaga para evaluarla en este momento. Pero, aquí hay algunas cosas a considerar.
CPS
De hecho, hay una manera de transformar cualquier código en un formulario que use solo llamadas de cola. Esta es la transformación CPS. CPS ( estilo de paso de continuación ) es una forma de expresar código pasando cada función a una continuación. Una continuación es una noción abstracta que representa "el resto de un cálculo". En el código expresado en forma CPS, la forma natural de reificar una continuación es como una función que acepta un valor. En CPS, en lugar de que una función devuelva un valor, aplica la función que representa la continuación actual al ser "devuelto" por la función.
Por ejemplo, considere la siguiente función:
Esto podría expresarse en CPS de la siguiente manera:
Es feo, y a menudo lento, pero tiene ciertas ventajas:
TCO
Me parece que la única razón para preocuparse por la recursividad de cola (o las llamadas de cola en general) es para fines de optimización de llamadas de cola (TCO). Por lo tanto, creo que una mejor pregunta es "¿mi código de rendimiento de transformación es optimizable por cola?".
Si consideramos una vez más CPS, una de sus características es que el código expresado en CPS consiste únicamente en llamadas de cola. Como todo es una llamada de cola, no necesitamos guardar un punto de retorno en la pila. Entonces, todo el código en forma de CPS debe ser optimizado, ¿verdad?
Bueno, no del todo. Verá, si bien puede parecer que hemos eliminado la pila, todo lo que hemos hecho es simplemente cambiar la forma en que la representamos. La pila ahora es parte del cierre que representa una continuación. Entonces, CPS no hace que todo nuestro código sea optimizado mágicamente.
Entonces, si CPS no puede hacer que todo sea TCO, ¿hay una transformación específica para la recursión directa que pueda? No, no en general. Algunas recursiones son lineales, pero otras no. Las recursiones no lineales (p. Ej., Árbol) simplemente deben mantener una cantidad variable de estado en alguna parte.
fuente