Có, một số ngôn ngữ và trình biên dịch sẽ chuyển đổi logic đệ quy thành logic không đệ quy. Điều này được gọi là tối ưu hóa cuộc gọi đuôi - lưu ý rằng không phải tất cả các cuộc gọi đệ quy đều được tối ưu hóa cuộc gọi đuôi. Trong tình huống này, trình biên dịch nhận ra một hàm có dạng:
int foo(n) {
...
return bar(n);
}
Ở đây, ngôn ngữ có thể nhận ra rằng kết quả được trả về là kết quả từ một hàm khác và thay đổi một lệnh gọi hàm với khung ngăn xếp mới thành một bước nhảy.
Nhận ra rằng phương pháp giai thừa cổ điển:
int factorial(n) {
if(n == 0) return 1;
if(n == 1) return 1;
return n * factorial(n - 1);
}
không phải là cuộc gọi đuôi tối ưu vì kiểm tra cần thiết trên trở lại.
Để thực hiện cuộc gọi đuôi này tối ưu hóa,
int _fact(int n, int acc) {
if(n == 1) return acc;
return _fact(n - 1, acc * n);
}
int factorial(int n) {
if(n == 0) return 1;
return _fact(n, 1);
}
Biên dịch mã này với gcc -O2 -S fact.c
(-O2 là cần thiết để cho phép tối ưu hóa trong trình biên dịch, nhưng với tối ưu hóa nhiều hơn -O3, con người khó đọc được ...)
_fact:
.LFB0:
.cfi_startproc
cmpl $1, %edi
movl %esi, %eax
je .L2
.p2align 4,,10
.p2align 3
.L4:
imull %edi, %eax
subl $1, %edi
cmpl $1, %edi
jne .L4
.L2:
rep
ret
.cfi_endproc
Người ta có thể thấy trong phân đoạn .L4
, jne
thay vì một call
(gọi một chương trình con với khung ngăn xếp mới).
Xin lưu ý rằng điều này đã được thực hiện với C. Tối ưu hóa cuộc gọi đuôi trong java rất khó và phụ thuộc vào việc triển khai JVM - đệ quy đuôi + java và đệ quy đuôi + tối ưu hóa là các bộ thẻ tốt để duyệt. Bạn có thể thấy các ngôn ngữ JVM khác có thể tối ưu hóa đệ quy đuôi tốt hơn (thử clojure (yêu cầu recur để tối ưu hóa cuộc gọi đuôi) hoặc scala).
return recursecall(args);
đệ quy, công cụ phức tạp hơn có thể bằng cách tạo một ngăn xếp rõ ràng và cuộn nó xuống, nhưng tôi nghi ngờ chúng sẽ