Comp 112

Lecture 10

Recursive Functions

2017.11.07

In its body, a function may call other functions.

One of those functions may be

*itself*.A function that calls itself in the process of computing its result is called a

**recursive function**.`def countdown (n) : """ signature: int -> NoneType precondition: n >= 0 """ if n == 0 : # a base case print ('go!') elif n > 0 : print (str (n)) countdown (n - 1) # a recursive call`

There is a conditional analyzing the argument(s) to decide whether the function should call itself.

A branch where the function doesn’t call itself is a

**base case**.A self-call of the function is a

**recursive call**.

Many programming problems can be solved by using either iteration or recursion:

```
def countdown_iter (n) :
while n > 0 :
print (str (n))
n -= 1
print ('go!')
```

$n \quad ⟼ \quad \overbrace{n , \underbrace{(n−1) , (n−2) , ⋯ , 1 , \mathrm{go!}} _ {\mathrm{countdown} ~ (n−1)} } ^ {\mathrm{countdown} ~ n}$

```
def countdown_rec (n) :
if n == 0 : # how to count down from zero:
print ('go!')
elif n > 0 : # how to count down from a positive number,
print (str (n))
countdown_rec (n - 1) # assuming we know how to count down from its predecessor
```

**iteration**solves problems by building up solutions using loops and accumulators or effects.**recursion**solves problems by breaking them down into similar but smaller problems with recursive function calls.**iteration**makes progress as loop variables get “closer” to failing the guard condition.**recursion**makes progress as the arguments to recursive calls get “closer” to a base case.**iteration**requires the programmer to structure subproblem solution composition. (“manual bookkeeping”)**recursion**uses the language’s function-calling mechanism to build up solutions from subproblem solutions. (“automatic bookkeeping”)**iteration**tackles problems from a “global” perspective.**recursion**tackles problems for a “local” perspective.

Recursion lets us “think locally and act globally”.

```
"""
signature: int -> int
precondition: n >= 0
returns the factorial of the argument
"""
```

```
def factorial_iter (n) : # from lecture 4
acc = 1
while n > 0 :
acc *= n
n -= 1
return acc
```

$n \quad ⟼ \quad \overbrace{n × \underbrace{(n−1) × (n−2) × ⋯ × 1} _ {\mathrm{factorial} ~ (n−1)} } ^ {\mathrm{factorial} ~ (n)}$

```
def factorial_rec (n) :
if n == 0 : # compute the factorial of zero:
return 1
elif n > 0 : # compute the factorial of a positive number,
factorial_of_pred = factorial_rec (n - 1) # assuming we know the factorial of its predecessor:
return n * factorial_of_pred
```

A common strategy for writing recursive functions for lists:

A list is either empty (

`[]`

), or else it’s not. This often provides the case distinction for recursive functions on lists.An empty list has no smaller slices, so it must be a base case.

A nonempty list must have at least one element, so there is always an element at index

`0`

. This element is called the**list head**:`head = xs [0]`

A nonempty list also has a slice beginning at index

`1`

and ending at the end of the list. This slice is called the**list tail**:`tail = xs [1 : ]`

The list tail is a list that is one element shorter than the original list. In this sense it’s “smaller”.

Often, a recursive call is made on the list tail.

This strategy also works for strings.

```
"""
signature: list (int) -> int
sums the numbers in a list
"""
```

```
def sum_iter (ns) :
acc = 0
for n in ns :
acc += n
return acc
```

$[n_0 , n_1 , n_2 , ⋯ , n_k] \quad ⟼ \quad \overbrace{n_0 + \underbrace{n_1 + n_2 + ⋯ + n_k} _ {\mathrm{sum} ~ (ns [1:])} } ^ {\mathrm{sum} ~ (ns)}$

```
def sum_rec (ns) :
if ns == [] : # sum of an empty list of numbers:
return 0
else : # sum a non-empty list of numbers,
tail_sum = sum_rec (ns [1 : ]) # assuming we know how to sum its tail:
return ns [0] + tail_sum
```

```
"""
signature: any a . list (a) -> list (a)
reverses a list of any type
"""
```

```
def reverse_iter (xs) : # from lecture 6
acc = []
for x in xs :
acc = [x] + acc
return acc
```

$[x_0 , x_1 , x_2 , ⋯ , x_n] \quad ⟼ \quad [x_n, ⋯ , x_2 , x_1 , x_0] ~ = ~ \overbrace{\underbrace{[x_n, ⋯ , x_2 , x_1]} _ {\mathrm{reverse} ~ (xs [1:])} + [x_0] } ^ {\mathrm{reverse} ~ (xs)}$

```
def reverse_rec (xs) :
if xs == [] : # reverse an empty list:
return []
else : # reverse a non-empty list,
reversed_tail = reverse_rec (xs [1 : ]) # assuming we know how to reverse its tail:
return reversed_tail + [xs [0]]
```

A **palindrome** is a word that reads the same forward as backward.

We can characterize the property of being a palindrome with the following specification:

The empty string is a palindrome.

A non-empty string is a palindrome if the first and last characters are equal and the rest of the characters, excluding the first and last, themselves form a palindrome.

This specification is recursive because in the second case we refer to the same property that we are describing.

It’s easy to turn this recursive specification into a recursive predicate function.

```
def is_palindrome (text) :
if text == '' :
return True
else :
return text [0] == text [-1] and is_palindrome (text [1 : -1])
```

A **self-similar** shape is one whose description is recursive, i.e. it contains a reference to itself.

Very simple recursive descriptions can produce interesting self-similar shapes

```
def tree (size , depth) :
# signature: int , int -> NoneType
# precondition: size >= 0 and depth >= 0
if depth == 0 :
pass
elif depth > 0 :
turtle.forward (size)
turtle.left (30.0)
tree (4/5 * size , depth - 1)
turtle.right (60.0)
tree (4/5 * size , depth - 1)
turtle.left (30.0)
turtle.back (size)
```

Here’s a helper-function for speeding up deeply-nested recursive drawings:

```
def faster (drawer , *args) :
delay = turtle.delay ()
frames = turtle.tracer()
turtle.tracer (300 , 100)
drawer (*args)
turtle.update ()
turtle.tracer (frames , delay)
```

e.g.

`faster (tree , 100 , 12) # a faster version of: tree (100 , 12)`

Starting with an equilateral triangle

we can draw a scaled-down version of it in each of its three corners

and scaled down version of those in each of their corners

and so on and so on…

The result is called a **Sierpinski triangle**:

```
def sierpinski_triangle (size , depth) :
# signature: int , int -> NoneType
# precondition: size >= 0 and depth >= 0
if depth == 0 :
pass
elif depth > 0 :
for i in range (3) : # for each of the three sides of a triangle:
sierpinski_triangle (size / 2 , depth - 1) # draw a scaled-down fractal triangle
turtle.forward (size) # draw an edge of the current triangle
turtle.left (120.0) # turn to create a corner of the current triangle
```

Once we have figured out its recursive structure, we can optimize it by pruning the empty branch:

```
def sierpinski_triangle (size , depth) :
if depth > 0 :
for i in range (3) :
sierpinski_triangle (size / 2 , depth - 1)
turtle.forward (size)
turtle.left (120.0)
```

Reading:

- sections 5.8–5.10: recursive functions (2 pages)
- sections 6.5–6.7: more recursion (3 pages)

Come to lab on Thursday.

Complete homework 10.