末尾再帰
実は、SICPのネタだったりする。
id:selvaggioとの雑談より。こういう話は、彼のほうが得意そうなのに・・・。
例として、次のようなコードがあったとする。
int f(int);
int g(int);int f(int i){
return g(i+i);
}int g(int i){
return i+1;
}void main(){
f(10);
// .....}
非末尾再帰
関数を呼び出すためには、ローカル変数をいったん別の場所に退避させる必要があります。
そして、ローカル変数はたいていスタック上に置かれるので、関数を呼び出すためには、スタックの保存・復元が必要となります。
なので、上記のコードは次のように実行されます。
- mainの現在のスタックを保存する
- mainがfを呼び出す
- fが現在のスタックを保存する
- fがgを呼び出す
- gがreturnする
- fがスタックを復元する
- fがreturnする
- mainがスタックを復元する
末尾再帰
上記のコードをよく見ると、fはgを呼び出したあと、ローカル変数を一度も使用していません。ローカル変数を使わないなら、わざわざスタックを復元する必要はありません。
さらに言えば、fはgの返り値を利用して計算を行っていないので、fにいったん制御を返す必要すらありません。
なので、この2つの処理を飛ばすと次のようになります。
- mainの現在のスタックを保存する
- mainがfを呼び出す
fが現在のスタックを保存する- fがgを呼び出す
- gがreturnする
fがスタックを復元するfがreturnする
- mainがスタックを復元する
こういう処理のことを末尾再帰って言う、・・・んだと思います。SICPの該当するページが見つからなかったから、記憶をたよりに書いてみたから、微妙かも。