# Lecture 29

## Recursion

MCS 260 Fall 2021
Emily Dumas

## Reminders

• Work on Project 3 ASAP. Do not delay!
• Project 3 autograder opens by Monday.
• Homework 10 posted; due Tuesday at 10am.
• ## OOP loose end: protocols

We implemented the sequence protocol last time. There are others.

Still more can be found in the collections.abc module, which contains classes you can subclass when implementing the protocols.

## Recursion

A function in Python can call itself. This can be useful, for example if the result of the function at one argument is easy to obtain from the result at another argument.

This technique is called recursion. A function which uses it is a recursive function.

## Factorial

The classic example of recursion (being easiest to understand) is the computation of factorials:

$n! = n \times (n-1) \times (n-2) \times \cdots 2 \times 1$

e.g. $5! = 5 \times 4 \times 3 \times 2 \times 1 = 120$

Critical observation: $n! = n \times (n-1)!$

## Recursive factorial

Let's build a function fact(n) that uses $n! = n \times (n-1)!$ as the basis of its operation.

## Call stack

Python keeps track of all the function calls that are underway in a stack. Items on the stack indicate where the call originated.

Calling a function pushes an item on the stack.

Returning pops an item form the stack.

There is a maximum allowed stack size. Exceeding it is a stack overflow.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30,
Called fact(2) on line 18
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30,
Called fact(2) on line 18,
Called fact(1) on line 18
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30,
Called fact(2) on line 18,
Called fact(1) on line 18 # returns 1
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30,
Called fact(2) on line 18
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30,
Called fact(2) on line 18 # returns 2
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
Called fact(3) on line 30 # returns 6
]


Note "top" of stack is the last element.

## Computing fact(3)

If push is list.append and pop is list.pop:

call_stack == [
]


Note "top" of stack is the last element.

## Recursive listdir

How can we make a function rlistdir(path) that will return a list of the contents of a directory and all of its subdirectories?

Python actually has multiple functions in the standard library that can do this, though we haven't discussed them. The point is to construct a solution using the things we've covered!

## Recursion pros and cons

Often can solve a problem with recursion or with loops (an iterative solution). Why use recursion?

Pros:

• Short code
• Clear code

Unclear:

• Speed

Cons:

• Uses more memory

### Acknowledgements

• Some of today's lecture was based on teaching materials developed for MCS 260 by Jan Verschelde.

### Revision history

• 2021-10-29 Initial publication