Да, некоторые языки и компиляторы преобразуют рекурсивную логику в нерекурсивную логику. Это известно как оптимизация хвостовых вызовов - обратите внимание, что не все рекурсивные вызовы являются оптимизируемыми хвостовыми вызовами. В этой ситуации компилятор распознает функцию вида:
int foo(n) {
...
return bar(n);
}
Здесь язык способен распознавать, что возвращаемый результат является результатом другой функции, и изменять вызов функции с новым кадром стека в переход.
Поймите, что классический метод факториала:
int factorial(n) {
if(n == 0) return 1;
if(n == 1) return 1;
return n * factorial(n - 1);
}
не является хвостовым вызовом, оптимизируемым из-за проверки, необходимой при возврате.
Чтобы сделать этот хвостовой вызов оптимизируемым,
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);
}
Компиляция этого кода с помощью gcc -O2 -S fact.c
(-O2 необходима для включения оптимизации в компиляторе, но с большей оптимизацией -O3 человеку становится трудно читать ...)
_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
Можно увидеть в сегменте .L4
, jne
а не call
(который выполняет вызов подпрограммы с новым кадром стека).
Обратите внимание, что это было сделано с C. Оптимизация вызовов Tail в java сложна и зависит от реализации JVM - tail-recursion + java и tail-recursion + оптимизация - хорошие наборы тегов для просмотра. Вы можете найти другие языки JVM способны оптимизировать хвостовую рекурсию лучше (попытка Clojure (который требует повторялся для оптимизации хвостового вызова), или Скале).
return recursecall(args);
для рекурсии, тем более сложный материал возможно путем создания явного стека и наматывая его вниз, но я сомневаюсь , что они будут