PHPにおける再帰呼び出しの動作原理と実行フロー

再帰関数とは、自身を直接または間接的に呼び出す関数です。この呼び出しはスタック構造に基づき、関数が新たなスコープを積み重ねながら進み、終了条件(ベースケース)に到達すると、積まれた各呼び出し層が逆順に復帰していきます。

再帰の正しく動作するためには、以下の3要素が不可欠です:

  • ベースケース(終了条件):再帰を停止させる明確な条件
  • 再帰ステップ:問題をより小さな同一構造のサブ問題へ分解する処理
  • 状態の変化:毎回の呼び出しでベースケースへ近づくように引数を更新

以下は、再帰の進行・復帰の両フェーズを可視化する改良版サンプルです:

<?php
function traceRecursion($depth, $maxDepth = 4) {
    // 【進行フェーズ】:深さを表示し、再帰呼び出し前に処理
    echo "→ 階層 {$depth}:開始\n";
    
    if ($depth < $maxDepth) {
        traceRecursion($depth + 1, $maxDepth);
    } else {
        echo "✓ 最深層 ({$depth}):終了条件に到達\n";
    }
    
    // 【復帰フェーズ】:呼び出し元へ戻った直後に実行
    echo "← 階層 {$depth}:復帰\n";
}

traceRecursion(0);
?>

出力結果:

→ 階層 0:開始  
→ 階層 1:開始  
→ 階層 2:開始  
→ 階層 3:開始  
→ 階層 4:開始  
✓ 最深層 (4):終了条件に到達  
← 階層 4:復帰  
← 階層 3:復帰  
← 階層 2:復帰  
← 階層 1:復帰  
← 階層 0:復帰

この出力から分かる通り、関数はまず「→」で示される下降過程でスタックに積まれ、ベースケースで停止した後、「←」で示される上昇過程で順次復帰します。各層の復帰タイミングで、呼び出し直後のコード(例ではecho "← ...")が実行されます。

もう一つの具体例として、入れ子関数による制御フローを示します:

<?php
function outer($x) {
    echo "[OUTER] 入力: {$x}\n";
    middle($x - 1);
    echo "[OUTER] 復帰: {$x}\n";
}

function middle($y) {
    echo "[MIDDLE] 入力: {$y}\n";
    inner($y - 1);
    echo "[MIDDLE] 復帰: {$y}\n";
}

function inner($z) {
    echo "[INNER] 入力: {$z}\n";
    // 終端:これ以上呼び出さない
}

outer(3);
?>

実行結果:

[OUTER] 入力: 3  
[MIDDLE] 入力: 2  
[INNER] 入力: 1  
[MIDDLE] 復帰: 2  
[OUTER] 復帰: 3

この例では、outer → middle → inner の順で関数が呼び出され、inner が終了すると middle の残り部分が実行され、さらにその完了で outer の残りが実行されます。これは再帰ではなく単なる関数の連鎖呼び出しですが、スタックによるLIFO(Last In, First Out)の制御フローを理解する上で有効です。

タグ: PHP Recursion call-stack base-case function-calling

8月8日 16:23 投稿