PageRenderTime 50ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/tail_recursion.py

https://bitbucket.org/pmatiello/tail-recursion
Python | 54 lines | 26 code | 13 blank | 15 comment | 7 complexity | 0774d8ce22955e8b9e930015f2ceaec5 MD5 | raw file
  1. '''
  2. Support for unlimited tail recursion for Python.
  3. @author: Pedro Matiello <pmatiello@gmail.com>
  4. '''
  5. from sys import _getframe as getframe
  6. from dis import opmap
  7. OPCODES = [chr(opmap['RETURN_VALUE']), chr(opmap['POP_TOP'])]
  8. class tail_recursive(object):
  9. """
  10. Tail recursion decorator.
  11. """
  12. def __init__(self, function):
  13. self.function = function
  14. def _is_tailcall(self, frame):
  15. """
  16. Analyze bytecode to verify whether the previous call is a tail call.
  17. References:
  18. 1. http://docs.python.org/release/2.7/library/dis.html#bytecodes
  19. 2. http://lambda-the-ultimate.org/node/1331#comment-15183
  20. """
  21. caller_frame = frame.f_back
  22. code = caller_frame.f_code.co_code
  23. ip = caller_frame.f_lasti
  24. return code[ip + 3] in OPCODES
  25. def __call__(self, *args, **kwargs):
  26. frame = getframe()
  27. if frame.f_back and frame.f_back.f_back and frame.f_back.f_back.f_code == frame.f_code and self._is_tailcall(frame):
  28. return _continue(self.function, *args, **kwargs)
  29. retval = self.function(*args, **kwargs)
  30. while (True):
  31. if (type(retval) == _continue):
  32. retval = retval.func(*retval.args, **retval.kwargs)
  33. else:
  34. return retval
  35. class _continue(object):
  36. def __init__(self, func, *args, **kwargs):
  37. self.func = func
  38. self.args = args
  39. self.kwargs = kwargs