Is there a way to check if function is recursive in python?

后端 未结 2 1905
无人及你
无人及你 2021-01-13 11:09

I want to write a testing function for an exercise, to make sure a function is implemented correctly.
So I got to wonder, is there a way, given a function \"foo\", to ch

相关标签:
2条回答
  • 2021-01-13 11:23

    I have not yet verified for myself if Alex's answer works (though I assume it does, and far better than what I'm about to propose), but if you want something a little simpler (and smaller) than that, you can simply use sys.getrecursionlimit() to error it out manually, then check for that within a function. For example, this is what I wrote for a recursion verification of my own:

    import sys
    
    def is_recursive(function, *args):
      try:
        # Calls the function with arguments
        function(sys.getrecursionlimit()+1, *args)
      # Catches RecursionError instances (means function is recursive)
      except RecursionError:
        return True
      # Catches everything else (may not mean function isn't recursive,
      # but it means we probably have a bug somewhere else in the code)
      except:
        return False
      # Return False if it didn't error out (means function isn't recursive)
      return False
    

    While it may be less elegant (and more faulty in some instances), this is far smaller than Alex's code and works reasonably well for most instances. The main drawback here is that with this approach, you're making your computer process through every recursion the function goes through until reaching the recursion limit. I suggest temporarily changing the recursion limit with sys.setrecursionlimit() while using this code to minimize the time taken to process through the recursions, like so:

    sys.setrecursionlimit(10)
    if is_recursive(my_func, ...):
      # do stuff
    else:
      # do other stuff
    sys.setrecursionlimit(1000) # 1000 is the default recursion limit
    
    0 讨论(0)
  • 2021-01-13 11:37

    Solution:

    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)
    

    Example usage/tests:

    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
    

    Essentially test_recursion takes a callable with no arguments, calls it, and returns True if at any point during the execution of that callable the same code appeared twice in the stack, False otherwise. I think it's possible that it'll turn out this isn't exactly what OP wants. It could be modified easily to test if, say, the same code appears in the stack 10 times at a particular moment.

    0 讨论(0)
提交回复
热议问题