Open In App

# Tail Recursion in Python Without Introspection

These are the special type of recursive functions, where the last statement executed inside the function is the call to the function itself.

### Advantages of implementing Tail Recursive function

• In this concept, the compiler doesn’t need to save the stack frame of the function after the tail-recursive call is executed.
• It uses less memory as compared to non-tail recursive function.
• It is faster as the compiler can do special optimization on tail-recursive function calls.

Note: In Python, the interpreter doesn’t perform any special optimization even if the function is a tail-recursive function.

## Example of Python Tail Recursive function Structure

In this example, we are just trying to emulate the structure of a tail-recursive function in Python. But in reality, this structure is the same as any other recursive function in Python.

## Python3

 `def` `count(n):``    ``if` `n < ``0``:``        ``return``    ``print``(``"Counting"``, n)``    ``count(n``-``1``)` `count(``3``)`

Output

```Counting 3
Counting 2
Counting 1
Counting 0
```

Time Complexity: O(n)
Auxiliary Space: O(n)

But, the above example, similar to any recursive function in Python, also has the disadvantage of limited recursive calls. To overcome this, we can convert the recursive structure of the function into an iterative structure using Python decorators.

## Python3

 `def` `count(n):``    ``if` `n < ``0``:``        ``return``    ``print``(``"Counting"``, n)``    ``count(n ``-` `1``)``    ` `count(``3``)`

Output

```Counting 3
Counting 2
Counting 1
Counting 0
```

Time Complexity: O(n)
Auxiliary Space: O(n)

## Python3

 `def` `count(n):``    ``if` `n < ``0``:``        ``return` `0``    ``print``(``"Counting"``, n)``    ``n ``-``=` `count(n``-``1``)``    ``return` `n` `count(``3``)`

Output

```Counting 3
Counting 2
Counting 1
Counting 0
```

Time Complexity: O(n)
Auxiliary Space: O(n)

## Python3

 `# class to define our own exception``class` `Recurse(Exception):``    ``def` `__init__(``self``, ``*``args: ``object``) ``-``> ``None``:``        ``super``(Recurse, ``self``).__init__()``        ``self``.args ``=` `args`  `# function to simulate the``# tail-recursive call``def` `recurse_sim(``*``args):``    ``raise` `Recurse(``*``args)`  `# decorator function to convert``# recursive -> iterative``def` `iterative_recursion(fn):``    ``def` `wrapper(``*``args):``        ``while` `1``:``            ``try``:``                ``return` `fn(``*``args)``            ``except` `Recurse as exc:``                ``args ``=` `exc.args` `    ``return` `wrapper`  `# tail recursive function``# implementation``@iterative_recursion``def` `count_pseudo_recursive(n):``    ``if` `n < ``0``:``        ``return` `    ``print``(``"Counting"``, n)``    ``return` `recurse_sim(n ``-` `1``)`  `# function call to start the``# tail recursion``count_pseudo_recursive(``3``)`

Output

```Counting 3
Counting 2
Counting 1
Counting 0
```

The above Python code gives output exactly the same as the normal Python code, with the tail-recursive structure. The only difference is, that this function can run for greater numbers without raising RecursionError in Python.

Explanation:

• Recurse exception class: This class is used to break the program flow, and also store the arguments function arguments to be used iteratively in the next call.
• recurse_sim function: This function is used to simulate the tail-recursive process and also raise the exception so that we can use arguments iteratively.
• iterative_recursion and wrapper function: This function is used as a decorator to convert the recursive process into an iterative process. The wrapper function handles the raised Recurse exception and uses the arguments from the exception class to call the same function with the collected arguments.
• count_pseudo_recursive function: This function is a pseudo-recursive function, that calls the recurse_sim function instead of itself to start the iterative process in a recursive way.