JavaScript | 再帰とループの性能比較

JavaScript JavaScript
スポンサーリンク

では「再帰を安全に使う工夫」について解説します。性能比較で見たように、再帰は便利だけど「深すぎる呼び出し」でスタックオーバーフローを起こす危険があります。そこで登場するのが 末尾再帰最適化トランポリン手法 です。


再帰を安全に使う工夫

1. 末尾再帰(Tail Recursion)

  • 定義: 関数の「最後の処理」が再帰呼び出しになっている形。
  • 一部の言語(Scheme, Scala, Rustなど)では「末尾再帰最適化」が働き、ループと同じ効率で実行できる。
  • 残念ながら JavaScriptは標準で末尾再帰最適化を保証していない ので、深すぎる再帰は危険。

例(末尾再帰版の階乗)

function factorialTail(n, acc = 1) {
  if (n === 0) return acc;          // 終了条件
  return factorialTail(n - 1, n * acc); // 最後に再帰呼び出し
}

console.log(factorialTail(5)); // 120
JavaScript

👉 acc(累積値)を引数で持ち回ることで、戻りながら計算する必要がなくなる。
👉 ただしJSでは最適化されないので、深すぎるとやはり危険。


2. トランポリン手法(Trampoline)

  • 考え方: 再帰関数が「次に呼ぶ関数」を返すようにして、実際の呼び出しはループで行う。
  • こうすると「関数呼び出しの深さ」が1で固定され、スタックオーバーフローを防げる。

例(トランポリン化した階乗)

function factorialStep(n, acc = 1) {
  if (n === 0) return acc;
  return () => factorialStep(n - 1, n * acc); // 関数を返す
}

function trampoline(fn) {
  while (typeof fn === "function") {
    fn = fn();
  }
  return fn;
}

console.log(trampoline(() => factorialStep(100000))); 
// 大きな数でもスタックオーバーフローしない
JavaScript

👉 trampoline が「関数を返す関数」をループで実行してくれるので、深い再帰も安全に処理できる。


3. 再帰をループに書き換える

  • 実務では「深さが大きい処理」はループに書き換えるのが一般的。
  • 例: 配列の合計、階乗などはループの方が高速で安全。

✅ まとめ

  • 末尾再帰: 戻り値の最後に再帰を置く → 一部言語では最適化される
  • トランポリン: 再帰を「関数を返す関数」に変えてループで処理 → JSでも安全
  • ループに変換: 実務ではシンプルで効率的

👉 ここまでで「再帰の安全な使い方」まで押さえられました。

タイトルとURLをコピーしました