【まつびよびだしさいてきか】
末尾呼び出し最適化 とは?
最終更新:
💡 呼び出しの後に仕事がなければ、お皿を使い回す
呼び出した関数の結果をそのまま返す場合に、呼び出し元のスタック領域を残さず実行する最適化。対応する処理系では、末尾再帰によるスタックの積み増しを抑えられます。
📌 このページのポイント
「末尾」って、一番下の行のこと?
行の位置より、呼び出した後に何をするかが大切だよ。例えば return g() は、gの結果をそのまま返す。一方 return 2 * g() なら、gから戻った後に掛け算が残るので末尾呼び出しではないんだ。
お皿を使い回すって、どういうこと?
普通の関数呼び出しでは、戻るための情報などをスタックに残す。お皿を重ねるようなイメージだね。末尾呼び出しでは呼び出し元の仕事が終わっているので、対応する処理系なら、その領域を再利用して次を実行できるんだ。
再帰を書けば、メモリは増えない?
JavaScriptやScalaでも使える?
JavaScriptの仕様にはProper Tail Callsという仕組みがあるけど、実装状況を確認しよう。WebKitではstrict modeなどの条件があり、MDNもJavaScriptCoreの対応を説明している。Scalaの@tailrecは、対象のメソッドをループへ最適化できないとコンパイルエラーにする確認用の注釈だよ。
まとめ:ざっくりこれだけ覚えればOK!
「末尾呼び出し最適化」って出てきたら「最後の関数呼び出しで、呼び出し用のメモリを使い回す工夫」と思えばだいたいOK!
📖 おまけ:英語の意味
「Tail Call Optimization」 = 末尾の関数呼び出しの最適化
💬 Tailは末尾、Callは関数呼び出しです。末尾再帰は、自分自身を末尾で呼ぶ場合。末尾呼び出しそのものは別の関数への呼び出しも含みます。