回答:
はい、一部の言語およびコンパイラは、再帰的ロジックを非再帰的ロジックに変換します。これは、末尾呼び出しの最適化として知られています-すべての再帰呼び出しが末尾呼び出しの最適化ではないことに注意してください。この状況では、コンパイラは次の形式の関数を認識します。
int foo(n) {
...
return bar(n);
}
ここで、言語は返される結果が別の関数からの結果であることを認識し、新しいスタックフレームを使用して関数呼び出しをジャンプに変更できます。
古典的な階乗法を実現する:
int factorial(n) {
if(n == 0) return 1;
if(n == 1) return 1;
return n * factorial(n - 1);
}
ではないので、復帰に必要な検査のテールコールoptimizatableは。
このテールコールを最適化するには、
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
(新しいスタックフレームでサブルーチン呼び出しを行う)ではなく、segment .L4
で見ることができます。jne
call
これはCで行われたことに注意してください。Javaでの末尾呼び出しの最適化は難しく、JVM実装に依存します。末尾再帰+ Javaおよび末尾再帰+最適化は、参照するのに適したタグセットです。あなたは他のJVM言語が最適化末尾再帰よりよい(必要とトライClojureの(することができます見つけることができRECUR末尾呼び出しの最適化への)、またはスカラ)。
慎重に踏んでください。
答えはイエスですが、常にではなく、すべてではありません。これはいくつかの異なる名前で行われる手法ですが、こことウィキペディアでかなり決定的な情報を見つけることができます。
私は「Tail Call Optimization」という名前を好みますが、他にも名前があり、一部の人々はこの用語を混同します。
とはいえ、実現すべき重要なことがいくつかあります。
テールコールを最適化するには、テールコールには、コールが行われた時点で既知のパラメーターが必要です。つまり、パラメーターの1つが関数自体の呼び出しである場合、ループに変換できません。これは、コンパイル時に展開できないループの任意のネストが必要になるためです。
C#は、末尾呼び出しを確実に最適化しません。ILには、F#コンパイラが発行する命令がありますが、C#コンパイラは一貫性なく発行し、JITの状況に応じて、JITはまったく発行しない場合もあります。すべての兆候は、C#で最適化されているテールコールに依存するべきではないということです、そうすることでオーバーフローのリスクは重大で現実です
return recursecall(args);
再帰のために、より複雑なものは、明示的なスタックを作成し、それを下巻きすることにより可能であるが、私は、彼らはないだろう