ПРЕДПОСЫЛКИ
Во время игры я часто пишу простые рекурсивные функции, которые выглядят примерно так:
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
(Например, при вычислении взвешенных расстояний редактирования или вычислении рекурсивно определенных математических формул.)
Затем я использую memoizing decorator для автоматического кэширования результатов.
ПРОБЛЕМА
Когда я пытаюсь что-то вроде f (200,10), я получаю:
RuntimeError: maximum recursion depth exceeded
Это так, как ожидалось, потому что рекурсивная реализация исчерпывает пределы пространства/рекурсии стека Python.
ДОРАБОТКИ
Я обычно работаю над этой проблемой одним из:
- Увеличение предела рекурсии с помощью sys.setrecursionlimit(работает только до 1000 глубины)
- Использование цикла for для заполнения кеша для меньших значений
- Изменение функции для использования списка в виде ручного стека (посредством добавления и поп-вызовов) (другими словами, переход от рекурсивной реализации к итеративной)
- Использование альтернативного языка программирования
но я нахожу все эти ошибки с ошибкой.
Вопрос
Есть ли способ написать декоратор @Bigstack, который будет имитировать эффект наличия действительно большого стека?
Обратите внимание, что мои функции обычно выполняют несколько рекурсивных вызовов функций, поэтому это не то же самое, что хвостовая рекурсия - я действительно хочу сохранить все внутреннее состояние каждой функции в стеке.
ЧТО Я ПОЛУЧИЛ
Я думал об использовании списка выражений генератора в качестве своего стека. Прощупывая стековый фрейм, я мог бы работать, когда функция была вызвана рекурсивно, а затем инициирует исключение, чтобы вернуться к коду декоратора. Тем не менее, я не могу разработать способ склеивания этих идей вместе, чтобы сделать что-нибудь, что действительно работает.
В качестве альтернативы я мог бы попытаться получить доступ к абстрактному синтаксическому дереву для функции и попробовать преобразовать вызовы к рекурсивным функциям для вывода операторов, но это похоже на то, что он движется в неправильном направлении.
Любые предложения?
ИЗМЕНИТЬ
Конечно, похоже, что я злоупотребляю Python, но другой подход, который я рассматривал, заключается в использовании другого потока для каждого блока, скажем, 500 кадров стека, а затем вставки очередей между каждой последовательной парой потоков - одна очередь для аргументов, и другую очередь для возвращаемых значений. (Каждая очередь будет содержать не более одной записи.) Я думаю, что это, вероятно, не работает по какой-то причине, но я, вероятно, буду только разбираться, почему после того, как я попытался ее реализовать.