Eliminación de llamadas de cola

Hemos discutido (en recursión de cola ) que una función recursiva es recursiva de cola si la llamada recursiva es lo último que ejecuta la función. 

C++

// An example of tail recursive function
void print(int n)
{
    if (n < 0) 
       return;
    cout << " " << n;
 
    // The last executed statement is recursive call
    print(n-1);
}

Java

// An example of tail recursive function
static void print(int n)
{
    if (n < 0) 
       return;
       
      System.out.print(" " + n);
 
    // The last executed statement
    // is recursive call
    print(n - 1);
}
 
// This code is contributed by rutvik_56

Python3

# An example of tail recursive function
def print(n):
     
    if (n < 0):
       return
    
    print(" ", n)
  
    # The last executed statement is recursive call
    print(n - 1)
 
# This code is contributed by sanjoy_62

C#

// An example of tail recursive function
static void print(int n)
{
    if (n < 0) 
       return;
       
      Console.Write(" " + n);
 
    // The last executed statement
    // is recursive call
    print(n - 1);
}
 
// This code is contributed by pratham76

Javascript

<script>
 
 
// An example of tail recursive function
function print(n)
{
    if (n < 0) 
       return;
    document.write( " " + n);
 
    // The last executed statement is recursive call
    print(n-1);
}
 
 
</script>

También discutimos que una cola recursiva es mejor que una no cola recursiva ya que los compiladores modernos pueden optimizar la cola recursiva. El compilador moderno básicamente realiza la eliminación de llamadas de cola para optimizar el código recursivo de cola . 

Si echamos un vistazo más de cerca a la función anterior, podemos eliminar la última llamada con goto. A continuación se muestran ejemplos de eliminación de llamadas de cola.

C++

// Above code after tail call elimination
void print(int n)
{
start:
    if (n < 0)
       return;
    cout << " " << n;
 
    // Update parameters of recursive call
    // and replace recursive call with goto
    n = n-1
    goto start;
}

QuickSort : un ejemplo más 
QuickSort también es recursivo de cola (tenga en cuenta que MergeSort no es recursivo de cola, esta es también una de las razones por las que QuickSort funciona mejor) 

C++

/* Tail recursive function for QuickSort
 arr[] --> Array to be sorted,
  low  --> Starting index,
  high  --> Ending index */
void quickSort(int arr[], int low, int high)
{
    if (low < high)
    {
        /* pi is partitioning index, arr[p] is now
           at right place */
        int pi = partition(arr, low, high);
 
        // Separately sort elements before
        // partition and after partition
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}
// See below link for complete running code
// http://geeksquiz.com/quick-sort/

Java

/* Java program to implement the above approach
 
   Tail recursive function for quick sort
   arr[] --> to get the elements
   low --> starting index
   high --> ending index
*/
static void quickSort(int[] arr, int low, int high)
{
    if (low < high)
    {
        /* pi is partitioning index,after the call
        to partition function the arr[p] will be
        at its correct index*/
        int pi = partition(arr, low, high);
 
        // Sort elements before and after
        // the partition separately
        // thus whole array will get sorted eventually
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}
 
// This code was contributed by Abhijeet Kumar

C#

// C# program to implement
// the above approach
using System;
 
class GFG{
     
 
/* Tail recursive function for QuickSort
 arr[] --> Array to be sorted,
  low  --> Starting index,
  high  --> Ending index */
static void quickSort(int[] arr, int low, int high)
{
    if (low < high)
    {
        /* pi is partitioning index, arr[p] is now
           at right place */
        int pi = partition(arr, low, high);
  
        // Separately sort elements before
        // partition and after partition
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}
}
 
// This code is contributed by code_hunt.

La función anterior se puede reemplazar siguiendo después de la eliminación de llamadas de cola. 

C++

/* QuickSort after tail call elimination
  arr[] --> Array to be sorted,
  low  --> Starting index,
  high  --> Ending index */
void quickSort(int arr[], int low, int high)
{
start:
    if (low < high)
    {
        /* pi is partitioning index, arr[p] is now
           at right place */
        int pi = partition(arr, low, high);
 
        // Separately sort elements before
        // partition and after partition
        quickSort(arr, low, pi - 1);
 
        // Update parameters of recursive call
        // and replace recursive call with goto
        low = pi+1;
        high = high;
        goto start;
    }
}
// See below link for complete running code
// https://ide.geeksforgeeks.org/dbq4yl

Por lo tanto, el trabajo de los compiladores es identificar la recursividad final, agregar una etiqueta al principio y actualizar los parámetros al final, seguido de agregar la última instrucción goto.

Gestión de marcos de pila de funciones en Tail Call Elimination: 
Recursion utiliza una pila para realizar un seguimiento de las llamadas de función. Con cada llamada de función, se inserta un nuevo marco en la pila que contiene variables locales y datos de esa llamada. Digamos que un marco de pila requiere O (1), es decir, espacio de memoria constante, entonces para N memoria de llamada recursiva requerida sería O (N). 

La eliminación de llamadas de cola reduce la complejidad espacial de la recursividad de O(N) a O(1). A medida que se elimina la llamada a la función, no se crean nuevos marcos de pila y la función se ejecuta en un espacio de memoria constante. 

Es posible que la función se ejecute en un espacio de memoria constante porque, en la función recursiva de cola, no hay declaraciones después de la declaración de llamada, por lo que no es necesario preservar el estado y el marco de la función principal. Se llama a la función secundaria y finaliza de inmediato, no tiene que devolver el control a la función principal. 

Como no se realiza ningún cálculo en el valor devuelto y no quedan declaraciones para su ejecución, el marco actual se puede modificar según los requisitos de la llamada de función actual. Por lo tanto, no hay necesidad de conservar marcos de pila de llamadas a funciones anteriores y ejecuciones de funciones en un espacio de memoria constante. Esto hace que la recursión de la cola sea más rápida y amigable con la memoria.

Artículo siguiente:  
QuickSort Tail Call Optimization (reducción del espacio en el peor de los casos para iniciar sesión)
Este artículo es una contribución de Dheeraj Jain . Escriba comentarios si encuentra algo incorrecto o si desea compartir más información sobre el tema tratado anteriormente.
 

Publicación traducida automáticamente

Artículo escrito por GeeksforGeeks-1 y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *