Podría ayudarlo a pensar sobre esto en términos de cómo se implementan realmente las optimizaciones de cola. Eso no es parte de la definición, por supuesto, pero sí motiva la definición.
Normalmente, cuando se invoca una función, el código de llamada almacenará los valores de registro que necesitará más adelante, en la pila. También almacenará una dirección de retorno, que indica la siguiente instrucción después de la llamada. Hará lo que sea necesario para asegurarse de que el puntero de pila esté configurado correctamente para el destinatario. Luego saltará a la dirección de destino [*] (en este caso, la misma función). A su regreso, sabe que el valor de retorno está en el lugar especificado por la convención de llamadas (registro o ranura de pila).
Para una llamada de cola, la persona que llama no hace esto. Ignora cualquier valor de registro, porque sabe que no lo necesitará más adelante. Configura el puntero de la pila para que el destinatario use la misma pila que la persona que llama, y no se configura como la dirección de retorno, simplemente salta a la dirección de destino.Por lo tanto, el destinatario sobrescribirá la misma región de pila, colocará su valor de retorno en la misma ubicación en que la persona que llama habría puesto su valor de retorno, y cuando vuelva, no regresará a su llamador, sino que regresará a su llamador llamador.
Por lo tanto, informalmente, una función es recursiva de cola cuando es posible que ocurra una optimización de cola de cola, y cuando el objetivo de la llamada de cola es la función misma. El efecto es más o menos el mismo que si la función contuviera un bucle, y en lugar de llamarse a sí mismo, la llamada de cola salta al inicio del bucle. Esto significa que no debe haber variables necesarias después de la llamada (y de hecho no hay "trabajo por hacer", que en un lenguaje como C++ no significa nada que destruir), y el llamador debe devolver el valor de retorno de la llamada final.
Esto es todo para recursión de cola simple/trivial. Hay transformaciones que pueden utilizarse para hacer algo recursivo que aún no lo es, por ejemplo, la introducción de parámetros adicionales, que almacenan cierta información utilizada por el nivel inferior de recursión, para hacer un trabajo que de otro modo se realizaría en el camino de salida". Así, por ejemplo:
int triangle(int n) {
if (n == 0) return 0;
return n + triangle(n-1);
}
se pueden hacer recursiva de cola, ya sea por el programador o automáticamente por un compilador lo suficientemente inteligente, así:
int triangle(int n, int accumulator = 0) {
if (n == 0) return accumulator;
return triangle(n-1, accumulator + n);
}
Por lo tanto, la primera función podría ser descrito como " cola recursiva "por alguien que está hablando de un lenguaje/compilador suficientemente inteligente. Prepárate para esa variante de uso.
[*] Almacenar una dirección de retorno, mover el puntero de la pila y saltar, puede o no estar envuelto en un solo código de operación por la arquitectura, pero incluso si no es lo que ocurre normalmente.