Understanding Recursion and Continuation with Python
Tags: python, recursion, programming
Published: 2020-05-11 16:44:09

---
title: Understanding Recursion and Continuation with Python
published: true
date: 2020-05-09 04:58:00 UTC
tags: Python,Recursion,Programming
canonical_url: https://coderscat.com/understanding-recursion-and-continuation-with-python
---

![photo-1554900773-4dd76725f876.jpeg](https://coderscat.com/wp-content/uploads/photo-1554900773-4dd76725f876.jpeg)

Figure 1: Photo by Amelie & Niklas Ohlrogge on Unsplash

In the article [How To Learn All Programming Languages](https://coderscat.com/learn-programming-languages), I explained learning programming language concepts is an effective way to master all programming language.

**Recursion** , **continuation** , and **continuation-passing style** are essential ideas for functional programming languages. Have an understanding of them will help much in knowing how programming languages work; even we don’t use them in daily programming tasks.

In this post, let’s learn these concepts of programming languages with some short Python programs.

## Recursion

> Recursion in computer science is a method of solving a problem where the solution depends on solutions to smaller instances of the same problem.

Most modern programming language support recursion by allowing a function to call itself from within its own code. Recursion is the default programming paradigm in many functional programming languages, such as Haskell, OCaml.

Many daily programming tasks or algorithms could be implemented in recursion in a simpler manner. Suppose you want to list all the files and sub-directories of a directory recursively, recursion will be a natural choice for implementation.

Let’s have a look at this simple recursive Python program:

```python
def fib_rec(n):
    if n < 2:
        return 1
    else:
        return fib_rec(n - 1) + fib_rec(n - 2)

print(fib_rec(5))

```

It is a naive implementation for computing Fibonacci numbers. A key point of recursion is there must be an exit point, the third line of `return 1` is the exit point for this program.

But what is the drawback of recursion?

Let’s add more debug information for this program, print the call depth in the beginning of function:

```python
import traceback
def fib_rec(n):
    print(len(traceback.extract_stack()) * '*' + ": " + str(n))
    if n < 2:
        return 1
    else:
        return fib_rec(n - 1) + fib_rec(n - 2)

print(fib_rec(5))

```

The output will be:

![2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_125520.png](https://coderscat.com/wp-content/uploads/2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_125520.png)

The `*` implies the call depths of the current function call. As we can see from the output, there are 2 points that need to notice:

1. There are duplicated computations during the whole progress. fib\_rec(3), fib\_rec(2), fib\_rec(1) are called multiple times.
2. The call stacks will grow quickly as the input number increase. Stack overflow exception will be thrown out if we want to compute fib\_rec(1000).

How could we fix these general issues of recursion?

## Tail Recursion

Tail recursion is a special form of recursion, in which the final action of a procedure calls itself again. In above program, the last action is `return 1` or `return fib_rec(n-1) + fib_rec(n-2)`, this is not a tail recursion.

Let’s try to convert above program to tail recursion:

```python
def fib_tail(n, acc1=1, acc2=1):
    print(len(traceback.extract_stack()) * '*' + ": " + str(n))
    if n < 2:
        return acc1
    else:
        return fib_tail(n - 1, acc1 + acc2, acc1)

print(fib_tail(5))

```

The output is like this:

![2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_174510.png](https://coderscat.com/wp-content/uploads/2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_174510.png)

From the result, we could find out we removed some duplicated computations, we solved the issue #1 of the above program.

### Tail Call Optimization (TCO)

There is a technical called [tail call optimization](https://en.wikipedia.org/wiki/Tail_call) which could solve the issue #2, and it’s implemented in many programming language’s compilers. But not implemented in Python. Guido explains why he doesn’t want `tail call optimization` [in this post](http://neopythonic.blogspot.com/2009/04/tail-recursion-elimination.html).

Anyway, let’s have an understanding of how `tail call optimization` works. We know that any call of sub-function will create a new stack frame during the execution. If we treat function call as a black box, we could reuse the same stack frame when the tail function call happens.

To do this, a compiler with `TCO` will try to eliminate the last tail call with a jump operation and fix the issue of stack overflow. Suppose if Python have a `goto` operation, we could replace the last call of `fib_tail` with `goto` and update the related parameters.

```python
# NOTE!!! This is pseudo-code

def fib_tail(n, acc1=1, acc2=1):
    START:
    if n < 2:
        return acc1
    else:
        #return fib_tail(n - 1, acc1 + acc2, acc1)
        n = n - 1
        tmp = acc1
        acc1 = acc1 + acc2
        acc2 = tmp
        goto START

```

From the result, the compiler actually could convert a recursive function into an iterative version. All iterative functions can be converted to recursion because iteration is just a special case of recursion (tail recursion).

This is the reason why many FP don’t perform poorly even we write code in recursive style. Compilers do their job!

## Continuation-passing style

And even more, functional programming languages adopt the [continuation-passing style](https://en.wikipedia.org/wiki/Continuation-passing_style) (CPS), in which control is passed explicitly in the form of a continuation.

A continuation is an abstract representation of the control state of a program.

Sounds obscure?

Let’s say continuation is a data structure that represents the computational process at a given a point in the process’s execution, we could save an execution state and continue the computational process latter.

Seems like `lambda function` in Python could be used for this, since we could pass a lambda function as parameters and call them later.

Let’s define a simplest continuation, this continuation will return the original value with any parameter:

```python
end_cont = lambda value: value

```

Then we try to convert the above fib\_tail function into a CPS. We add a extra parameter called `cont`:

```python
def fib_cps(n, cont):
    print(len(traceback.extract_stack()) * '*' + ": " + str(n))
    if n < 2:
        return cont(1)
    else:
        return lambda: fib_cps(
                         n - 1,
                         lambda value:
                           lambda: fib_cps(
                                     n - 2,
                                     lambda value2:
                                       lambda: cont(value + value2)))
print(fib_cps(5, end_cont))

```

The output is:

```
<function <lambda> at 0x101d52758>
```

Emm, we only got a lambda function as a result. Remember we could continue to execute a continuation, so we continue to run this lambda function, the returned value is still a continuation …

```python
v = fib_cps(5, end_cont)
print(v)

print(v)
v = v()
print(v)

v = v()
print(v)

v = v()
print(v)

....

**: 5
<function <lambda> at 0x10d493758>
***: 4
<function <lambda> at 0x10d493848>
***: 3
<function <lambda> at 0x10d4938c0>
***: 2
<function <lambda> at 0x10d493938>
***: 1

```

Let’s wrap a function to call `v` repatedly until we got a real value:

```python
def trampoline(f, *args):
    v = f(*args)
    while callable(v):
        v = v()
    return v

```

Then run it with:

```python
print(trampoline(fib_cps, 5, end_cont))

```

![2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_190057.png](https://coderscat.com/wp-content/uploads/2020_05_08_understanding-tail-recursive-and-csp-with-python.org_20200509_190057.png)

Woo! The stack depth always keeps the same during the execution procedure. Some compilers of functional programming languages will do CPS-transform automatically.

Continuations are also useful for implementing other control mechanisms in programming languages such as exceptions, generators, coroutines, and so on.

## Summary up

We have a little but real experience of tail recursion, tail call optimization, and continuation. I know we won’t write code with this style in daily programming. But these concepts helps me to understand programming languages deeper, and they are fun!

I hope you enjoy it.

The post [Understanding Recursion and Continuation with Python](https://coderscat.com/understanding-recursion-and-continuation-with-python) appeared first on [Coder's Cat](https://coderscat.com).