ソリューション:
from bdb import Bdb
import sys
class RecursionDetected(Exception):
pass
class RecursionDetector(Bdb):
def do_clear(self, arg):
pass
def __init__(self, *args):
Bdb.__init__(self, *args)
self.stack = set()
def user_call(self, frame, argument_list):
code = frame.f_code
if code in self.stack:
raise RecursionDetected
self.stack.add(code)
def user_return(self, frame, return_value):
self.stack.remove(frame.f_code)
def test_recursion(func):
detector = RecursionDetector()
detector.set_trace()
try:
func()
except RecursionDetected:
return True
else:
return False
finally:
sys.settrace(None)
使用例/テスト:基本的に
def factorial_recursive(x):
def inner(n):
if n == 0:
return 1
return n * factorial_recursive(n - 1)
return inner(x)
def factorial_iterative(n):
product = 1
for i in xrange(1, n+1):
product *= i
return product
assert test_recursion(lambda: factorial_recursive(5))
assert not test_recursion(lambda: factorial_iterative(5))
assert not test_recursion(lambda: map(factorial_iterative, range(5)))
assert factorial_iterative(5) == factorial_recursive(5) == 120
test_recursion
は、引数なしで呼び出し可能になります、それを呼び出し、その呼び出し可能の実行中の任意の時点であればTrue
を返します。同じコードがスタックに2回、それ以外の場合はFalse
になりました。私はそれがOPが望んでいるものではないことが判明する可能性が高いと思います。例えば、特定の瞬間に同じコードがスタックに10回現れるかどうかをテストするために簡単に変更できます。
「foo()」を呼び出す 'foo()'を定義したとしましょう。 'bar = foo'と言って新しい' foo() 'を定義します。 'bar()'を呼び出すと、それ自身ではなく、 'foo'を呼び出します。この関数は名前を持つ関数を呼び出すため再帰的ですが、同じ名前の別の関数になる可能性があります。 – zondo
@AlexHall:Pythonでの再帰をチェックする唯一の方法です*、実際に起こっているとき*。再帰呼び出しに使用される名前はいつでも*何かにリバウンドする可能性があるためです。 –
@MartijnPieters少なくともOPの問題の解決策は、ほとんどの場合に機能します。ゾンドの異議は非常に具体的であり、必ずしもOPには関係しません。 –