Co to jest przywołanie ogona?

W programowaniu komputerowym, tail call to specyficzna sytuacja w kodzie źródłowym programu, w której funkcja, podprogram lub procedura zwraca oczekiwaną wartość przez wywołanie innej funkcji zamiast po prostu przekazać zmienną przechowującą zwracaną wartość. Sama nazwa wskazuje, że funkcja wywoływana w celu obliczenia wartości do zwrócenia znajduje się na końcu lub ogonie funkcji wywołującej ją w celu dostarczenia wartości zwracanej. Wywołanie końcowe jest interesujące dla niektórych programistów, ponieważ przy pewnych optymalizacjach lub zachowaniach kompilatora żadne dodatkowe miejsce na stosie nie jest używane do przechowywania lokalizacji kodu funkcji głównej; funkcja tail zamiast tego jest używana do generowania raportów wartości zwracanych bezpośrednio z powrotem do punktu wywołania, w którym została wywołana oryginalna funkcja. Użycie wywołania końcowego jest szczególnie przydatne w sytuacjach, w których stosowana jest rekurencja, ponieważ ilość miejsca na stosie używanego do przechowywania adresów wywołujących w przypadkach, w których wywołania rekurencyjne są bardzo głęboko zagnieżdżone, może szybko się wyczerpać i zatrzymać wykonywanie programu. Chociaż używanie wywołań końcowych może pomóc w zwiększeniu szybkości, wykorzystania pamięci i wydajności w programie, może również prowadzić do sytuacji, w których kod źródłowy zostanie zrestrukturyzowany, aby używać wywołań w sposób, który utrudnia debugowanie i śledzenie, zwłaszcza w przypadkach rekurencja.

Istnienie wywołania końcowego wynika w dużej mierze z tego, jak stos wywołań działa w większości programów komputerowych i architektur systemowych. Stos, który jest jak stos talerzy, jest strukturą danych typu „pierwsze weszło, ostatnie wyszło”. Kiedy wywoływana jest funkcja, podprogram lub procedura, adres, z którego wykonywane jest wywołanie, zwany ramką stosu, jest przechowywany na stosie. Oznacza to, że program, który wywołuje funkcję A, a następnie wywołuje funkcję B, będzie miał dwie ramki stosu, jedną dla funkcji B, a drugą pod nią dla funkcji A. Po zakończeniu działania funkcji B jej ramka stosu jest wysuwana z góry stos i wykonanie powracają do Funkcji A, której ramka jest usuwana ze stosu, gdy jest skończona, ostatecznie zwracając sterowanie programem do punktu, z którego pierwsza funkcja została pierwotnie wywołana.

Gdy używane jest wywołanie końcowe, instrukcja return w funkcji bezpośrednio używa wartości zwracanej innej funkcji jako danych, które mają zostać wysłane do kodu wywołującego. W powyższym przykładzie, jeśli funkcja A wywołuje funkcję B bezpośrednio za pomocą instrukcji return, utworzono wywołanie końcowe. Wewnątrz stosu wywołań, zamiast ramki stosu dla obu funkcji A i B, funkcja B otrzyma adres zwrotny z funkcji A, a ramka stosu funkcji A zostanie usunięta i usunięta, co oznacza, że ​​funkcja B przekaże wartość zwracaną bezpośrednio z powrotem do lokalizacji, która wywołała funkcję A bez konieczności wcześniejszego przekazywania kontroli z powrotem do funkcji A. Zwiększa to szybkość wywołań funkcji, a także pomaga ograniczyć ilość informacji na stosie.

Właściwości wywołania ogona mogą uczynić je bardzo atrakcyjną opcją dla funkcji rekurencyjnych. Funkcja rekurencyjna to taka, która wielokrotnie wywołuje samą siebie w celu obliczenia wartości, co może mieć miejsce podczas przechodzenia przez strukturę danych listy. Dla wywołań funkcji zagnieżdżonych nie są tworzone żadne dodatkowe ramki stosu, więc bardzo głębokie poziomy rekurencji mogą być wykonywane bezpiecznie bez bezpośredniego zagrożenia przepełnieniem stosu i możliwym zakończeniem programu.