Nó có thể giúp bạn suy nghĩ về điều này về cách tối ưu hóa cuộc gọi đuôi thực sự được triển khai. Đó không phải là một phần của định nghĩa, tất nhiên, nhưng nó thúc đẩy định nghĩa.
Thông thường khi một hàm được gọi, mã gọi sẽ lưu trữ bất kỳ giá trị đăng ký nào mà nó sẽ cần sau này, trên ngăn xếp. Nó cũng sẽ lưu một địa chỉ trả về, cho biết lệnh tiếp theo sau cuộc gọi. Nó sẽ làm bất cứ điều gì nó cần làm để đảm bảo rằng con trỏ ngăn xếp được thiết lập chính xác cho các callee. Sau đó, nó sẽ nhảy đến địa chỉ đích [*] (trong trường hợp này, cùng chức năng). Ngày trở lại, nó biết giá trị trả về là ở nơi được quy định bởi quy ước gọi (đăng ký hoặc ngăn xếp ngăn xếp).
Để biết cuộc gọi đuôi, người gọi không thực hiện việc này. Nó bỏ qua bất kỳ giá trị đăng ký nào, bởi vì nó biết nó sẽ không cần chúng sau này. Nó thiết lập con trỏ ngăn xếp để các callee sẽ sử dụng cùng một ngăn xếp người gọi đã làm, và nó không thiết lập chính nó như là địa chỉ trả về, nó chỉ nhảy đến địa chỉ đích.Do đó, callee sẽ ghi đè lên cùng một vùng stack, nó sẽ đặt giá trị trả về của nó trong cùng một vị trí mà người gọi sẽ đặt giá trị trả về của nó, và khi nó trả về, nó sẽ không trả về người gọi, nhưng sẽ quay lại người gọi người gọi.
Do đó, không chính thức, hàm sẽ đệ quy đuôi khi có thể tối ưu hóa cuộc gọi đuôi và khi đích của cuộc gọi đuôi là chính hàm đó. Hiệu ứng nhiều hơn hoặc ít hơn giống như chức năng chứa một vòng lặp, và thay vì gọi chính nó, cuộc gọi đuôi nhảy đến đầu vòng lặp. Điều này có nghĩa là không có biến cần thiết sau khi cuộc gọi (và thực sự không "làm việc", mà trong một ngôn ngữ như C++ có nghĩa là không có gì bị hủy), và giá trị trả về của cuộc gọi đuôi phải được trả về bởi người gọi.
Đây là tất cả để đệ quy đuôi đơn giản/tầm thường. Có các phép biến đổi có thể được sử dụng để tạo ra một cái gì đó có tính đệ quy không được, ví dụ giới thiệu các tham số phụ, lưu trữ một số thông tin được sử dụng bởi mức đệ quy "dưới cùng", để thực hiện công việc mà nếu không sẽ được thực hiện "lối thoát". Vì vậy, ví dụ:
int triangle(int n) {
if (n == 0) return 0;
return n + triangle(n-1);
}
thể được thực hiện đuôi-đệ quy, hoặc bằng cách lập trình viên hoặc tự động bởi một trình biên dịch đủ thông minh, như thế này:
int triangle(int n, int accumulator = 0) {
if (n == 0) return accumulator;
return triangle(n-1, accumulator + n);
}
Do đó, chức năng cũ có thể được mô tả như " đuôi đệ quy "bởi một người nói về một ngôn ngữ đủ thông minh/trình biên dịch. Hãy chuẩn bị cho việc sử dụng biến thể đó.
[*] Lưu trữ địa chỉ trả lại, di chuyển con trỏ ngăn xếp và nhảy, có thể hoặc không được gói trong một mã vạch duy nhất bởi kiến trúc, nhưng ngay cả khi đó không phải là điều thường xảy ra.