背景
在玩耍时,我经常编写简单的递归函数,如下所示:
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 循环为较小的值填充缓存
- 更改函数以将列表用作手动堆栈(通过追加和弹出调用)(换句话说,从递归实现移动到迭代实现)
- 使用另一种编程语言
但我发现所有这些都很容易出错。
问题
有没有办法编写一个@Bigstack 装饰器来模拟拥有一个非常大的堆栈的效果?
请注意,我的函数通常会进行多次递归函数调用,因此这与尾递归不同 - 我确实希望将每个函数的所有内部状态保存在堆栈中。
我试过的
我一直在考虑使用生成器表达式列表作为我的堆栈。通过探测堆栈帧,我可以确定函数何时被递归调用,然后触发异常以返回装饰器代码。但是,我无法想出一种方法将这些想法粘合在一起以制造出真正有效的东西。
或者,我可以尝试访问函数的抽象语法树,并尝试将调用转换为递归函数以产生语句,但这似乎是朝着错误的方向前进。
有什么建议么?
编辑
看起来我确实在滥用 Python,但我一直在考虑的另一种方法是为每个块(例如 500 个堆栈帧)使用不同的线程,然后在每对连续的线程之间插入队列——一个用于参数的队列,另一个用于返回值的队列。(每个队列最多有一个条目。)我认为这可能由于某种原因不起作用 - 但我可能只会在我尝试实现它之后才能弄清楚为什么。