再帰の終了条件が無いときの原因と直し方
再帰関数に終了条件(ベースケース)が無い、または正しく機能していないときに発生します。再帰は必ずどこかで止まる条件が必要です。
エラーメッセージの読み方
Traceback (most recent call last):
main.py- ファイル名
4- 行番号 — 実際にクラッシュした行
countdown- 発生場所 — <module> ならトップレベル、関数名ならその関数の中
RecursionError- 例外クラス — 何が起きたか。ここを検索するのが最短です
maximum recursion depth exceeded- 詳細メッセージ — どの値が問題だったか
このエラーが出る典型パターン
パターン1
1 def countdown(n): 2 ^ 3 print(n) 4 return countdown(n - 1) 5 6 countdown(5)
終了条件(ベースケース)が無いと、再帰は自分自身を呼び続けます。Pythonは既定の再帰深度を超えると例外で止めます。
直し方: (空) を if n < 0: return にします。
パターン2
1 def factorial(n): 2 ^ 3 return n * factorial(n - 1) 4 5 print(factorial(5))
階乗の計算は定番の再帰例ですが、n<=1で止める条件を書き忘れると永遠に自分自身を呼び続けます。
直し方: (空) を if n <= 1: return 1 にします。
パターン3
1 def sum_up_to(n): 2 ^ 3 return n + sum_up_to(n - 1) 4 5 print(sum_up_to(5))
1からnまでの合計を再帰で求める場合も同様です。0以下になったら足すのをやめる条件が必要です。
直し方: (空) を if n <= 0: return 0 にします。
パターン4
1 def fibonacci(n): 2 ^ 3 return fibonacci(n - 1) + fibonacci(n - 2) 4 5 print(fibonacci(5))
フィボナッチ数列の再帰でも同じです。小さいnで止める条件が無いと、マイナス方向にどこまでも呼び出しが続きます。
直し方: (空) を if n <= 1: return n にします。
パターン5
1 def power(base, exp): 2 ^ 3 return base * power(base, exp - 1) 4 5 print(power(2, 5))
べき乗の再帰計算も同じ構造です。指数が0になったら1を返して止める条件が必要です。
直し方: (空) を if exp <= 0: return 1 にします。
よくある誤解
無限ループと違い、再帰の暴走はスタックが尽きるまで気づかれないことがあります。Pythonは既定で再帰の深さに上限を設けており、超えると例外で止めてくれます。
実務での勘所
デフォルトの再帰上限(sys.getrecursionlimit()で確認でき、既定は1000)は、理論的な限界ではなく、Pythonを実行しているCの呼び出しスタックが尽きてクラッシュする前に安全に止めるための人為的な安全マージンです。sys.setrecursionlimit()で上限を引き上げること自体は可能ですが、際限なく上げるとPythonの例外ではなくOSレベルのスタックオーバーフローでプロセスごと落ちる危険があります。また、Pythonは末尾再帰の最適化を行わない言語なので、深い再帰が本質的に必要な処理は、上限を上げるのではなくループに書き直すのが安全な解決策です。