バックグラウンド
遊んでいるとき、私はよく次のような単純な再帰関数を記述します。
def f(a,b):
if a>=0 and b>=0:
return min( f(a-1,b) , f(b,a-1) ) # + some cost that depends on a,b
else:
return 0
(たとえば、加重編集距離を計算する場合、または再帰的に定義された数式を評価する場合。)
次に、メモ化デコレータを使用して、結果を自動的にキャッシュします。
問題
f(200,10)のようなものを試してみると、次のようになります。
RuntimeError: maximum recursion depth exceeded
再帰的な実装はPythonのスタックスペース/再帰の制限を使い果たすため、これは予想どおりです。
回避策
私は通常、次のいずれかでこの問題を回避します。
- sys.setrecursionlimitを使用して再帰制限を増やす(約1000の深さまでしか機能しません)
- forループを使用して、小さい値のキャッシュをいっぱいにします
- リストを手動スタックとして使用するように関数を変更する(appendおよびpop呼び出しを介して)(つまり、再帰的な実装から反復的な実装に移行する)
- 代替プログラミング言語の使用
しかし、これらはすべてエラーが発生しやすいと思います。
質問
本当に大きなスタックを持つことの効果をシミュレートする@Bigstackデコレータを作成する方法はありますか?
私の関数は通常、複数の再帰関数呼び出しを行うため、これは末尾再帰と同じではないことに注意してください。実際には、各関数のすべての内部状態をスタックに保存したいと思います。
私が試したこと
ジェネレータ式のリストをスタックとして使用することを考えていました。スタックフレームをプローブすることで、関数が再帰的に呼び出されたときに解決し、例外をトリガーしてデコレーターコードに戻ることができます。ただし、これらのアイデアを組み合わせて実際に機能するものを作成する方法を見つけることはできません。
または、関数の抽象構文ツリーにアクセスして、再帰関数への呼び出しを変換してステートメントを生成することもできますが、これは間違った方向に向かっているようです。
助言がありますか?
編集
確かにPythonを誤用しているように見えますが、私が検討している別のアプローチは、たとえば500スタックフレームのブロックごとに異なるスレッドを使用してから、連続するスレッドの各ペアの間にキューを挿入することです。戻り値のキュー。(各キューには最大で1つのエントリが含まれます。)これはおそらく何らかの理由で機能しないと思いますが、実装を試みた後で理由を理解するだけです。