Устранение хвостового крика

Опубликовано: 20 Января, 2022

We have discussed (in tail recursion) that a recursive function is tail recursive if the recursive call is the last thing executed by the function. 

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>

Мы также обсудили, что хвостовая рекурсия лучше, чем не хвостовая рекурсия, поскольку хвостовая рекурсия может быть оптимизирована современными компиляторами. Современный компилятор в основном делает устранение хвостового вызова для оптимизации кода с хвостовой рекурсией.

If we take a closer look at the above function, we can remove the last call with goto. Below are examples of tail call elimination.

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 : One more example 
QuickSort is also tail recursive (Note that MergeSort is not tail recursive, this is also one of the reasons why QuickSort performs better) 

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

Вышеупомянутую функцию можно заменить следующей после устранения хвостового вызова.

Поэтому задача компиляторов - определить хвостовую рекурсию, добавить метку в начале и обновить параметр (ы) в конце, а затем добавить последний оператор goto.

Управление кадрами стека функций в устранении хвостового вызова:
Рекурсия использует стек для отслеживания вызовов функций. При каждом вызове функции в стек помещается новый фрейм, содержащий локальные переменные и данные этого вызова. Скажем, для одного кадра стека требуется O (1), т. Е. Постоянное пространство памяти, тогда для N рекурсивных вызовов требуется память O (N).

Устранение хвостового вызова снижает пространственную сложность рекурсии с O (N) до O (1). Поскольку вызов функции исключен, новые кадры стека не создаются, и функция выполняется в постоянном пространстве памяти.

Функция может выполняться в постоянном пространстве памяти, потому что в хвостовой рекурсивной функции нет операторов после оператора вызова, поэтому сохранение состояния и кадра родительской функции не требуется. Дочерняя функция вызывается и немедленно завершается, ей не нужно возвращать управление родительской функции.

Поскольку для возвращаемого значения не выполняются вычисления и не остается никаких операторов для выполнения, текущий кадр может быть изменен в соответствии с требованиями текущего вызова функции. Таким образом, нет необходимости сохранять кадры стека предыдущих вызовов функций, и функция выполняется в постоянном пространстве памяти. Это делает хвостовую рекурсию более быстрой и удобной для памяти.

Следующая статья:
Оптимизация вызовов QuickSort Tail (сокращение места для журнала в худшем случае)
Эта статья предоставлена Дираджем Джайном . Пожалуйста, напишите комментарии, если вы обнаружите что-то неправильное, или вы хотите поделиться дополнительной информацией по теме, обсужденной выше

Вниманию читателя! Не прекращайте учиться сейчас. Освойте все важные концепции DSA с помощью самостоятельного курса DSA по приемлемой для студентов цене и будьте готовы к работе в отрасли. Чтобы завершить подготовку от изучения языка к DS Algo и многому другому, см. Полный курс подготовки к собеседованию .

Если вы хотите посещать живые занятия с отраслевыми экспертами, пожалуйста, обращайтесь к Geeks Classes Live и Geeks Classes Live USA.