chapter 3 programming with recursion - uppsala · pdf filech.3: programming with recursion...

26
Ch.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples .................................................. 3.2 2. Correctness ................................................ 3.5 3. Construction methodology ................................ 3.13 4. Forms of recursion ........................................ 3.15 Sven-Olof Nystr¨om/IT Dept/Uppsala University FP 3.1

Upload: lythu

Post on 29-Mar-2018

239 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion Plan

Chapter 3

Programming with Recursion

1. Examples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.2

2. Correctness . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.5

3. Construction methodology . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.13

4. Forms of recursion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3.15

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.1

Page 2: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.1. Examples

3.1. Examples

Factorial (revisited from Section 2.13)

Program (fact.sml)

fun fact n =

if n < 0 then error "fact: negative argument"

else if n = 0 then 1

else n ∗ fact (n−1)

Useless test of the error case at each recursive call!

Hence we introduce an auxiliary function,

and can then use pattern matching in its declaration:

fun factAux 0 = 1

| factAux n = n ∗ factAux (n−1)

fun fact1 n =

if n < 0 then error "fact1: negative argument"

else factAux n

In fact1: pre-condition verification (defensive programming)

In factAux: no pre-condition verification

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.2

Page 3: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.1. Examples

Exponentiation

Specification

function expo x n

TYPE: real → int → real

PRE: n ≥ 0

POST: xn

Construction

Error case: n < 0: produce an error message

Base case: n = 0 : return 1

General case: n > 0 :

return xn = x ∗ xn−1 = x ∗ expo x (n−1)

Program (expo.sml)

fun expoAux x 0 = 1.0

| expoAux x n = x ∗ expoAux x (n−1)

fun expo x n =

if n < 0 then error "expo: negative argument"

else expoAux x n

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.3

Page 4: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.1. Examples

Triangle

Specification

function triangle a b

TYPE: int → int → int

PRE: (none)

POST:∑

a ≤ i ≤ b

i

Construction

Base case: a > b : return 0

General case: a ≤ b :

return∑

a≤i≤b

i = a +∑

a+1≤i≤b

i = a + triangle (a+1) b

Program (triangle.sml)

fun triangle a b =

if a > b then 0

else a + triangle (a+1) b

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.4

Page 5: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

3.2. Correctness

How do we know that a recursive program computes what we want?

Lets try a very simple program.

fun f n =

if n = 0 then 0

else 1 + f(n-1)

What does f compute?

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.5

Page 6: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Correctness (cont)

What does this function compute?

fun f n =

if n = 0 then 0

else 1 + f(n-1)

Answer:

f n = n, if n>=0

Seems reasonable, but how do we prove it?

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.6

Page 7: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Proof by induction

For all n >= 0, f(n) = n.

Base case: n = 0.

f(n) = 0 = n follows immediately.

Induction step:

Hypothesis: f(k) = k, for all k such that 0 <= k < n

We have

f(n) = 1 + f(n − 1), by definition

1 + f(n − 1) = 1 + (n − 1), by induction hypothesis

1 + (n − 1) = n, by algebraic transformations

Thus f(n) = n

It follows that f(n) = n, for all n >= 0.

Question: What can we say about f(−1)?

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.7

Page 8: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Another example, slightly harder

What does this function compute?

fun g(0) = 0

| g(n) = n + g(n)

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.8

Page 9: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Another example (cont)

What does this function compute?

fun g(0) = 0

| g(n) = n + g(n)

Answer: g(n) = (n ∗ n + n)/2

Proof by induction.

For all n >= 0, g(n) = (n ∗ n + n)/2

Base case:

n = 0.

g(n) = (n ∗ n + n)/2 by definition

(n ∗ n + n)/2 = (0 ∗ 0 + 0)/2 = 0

Induction step

Hypothesis: g(k) = (k ∗ k + k)/2, for all k such that 0 <= k < n

We have

g(n) = n + g(n − 1), by definition

= n + ((n − 1) ∗ (n − 1) + (n − 1))/2, by induction hypothesis

= n + (n ∗ n − 2 ∗ n + 1 + n − 1)/2, by algebraic transformations

= n + (n ∗ n − n)/2 − n, simplification

= (n ∗ n + n)/2

Thus g(n) = (n ∗ n + n)/2

It follows that g(n) = (n ∗ n + n)/2, for all n >= 0.

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.9

Page 10: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

If you think these examples where too simple

Exercises:

• Prove that the factorial given earlier produces the intended results.

• Prove the same for the gcd function.

What do the functions below compute? Make an educated guess (try on

an SML system if you like) and show by induction that your guess was

correct.

fun h(n) = if n = 0 then 0 else h(n-1)+2*n-1;

fun m(a,b) = if a = 0 then 0 else b+m(a-1,b);

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.10

Page 11: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Correctness of functional programs

Suppose we have a recursive function

fun f(x) = ... f(..) ...

and we want to show that it satisfies some property P (x, f(x))

Solution:

• Show that the function terminates (for all values of x that we are

interested in)

• Assume that all recursive calls satisfy the property P (. . . , f(. . .)).

Show P (x, f(x)).

Then we have shown that P (x, f(x)) holds for all values of x we are

interested in. (This is just an induction proof in disguise.)

(By the way, showing that if an answer is returned it will be correct is

also known as partial correctness.)

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.11

Page 12: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.2. Correctness

Example (factorial):

fun fac(x) =

if x = 0

then 1

else x*fac(x-1)

Assume that fac(x) terminates for all x >= 0. Show that

P (x, fac(x)) <=> fac(x) = 1 ∗ 2 ∗ . . . ∗ x holds.

Assuming property holds for recursive calls...

By the definition of the function fac we have

fac(x) = if x = 1 then 0 else x ∗ (1 ∗ 2 ∗ . . . ∗ (x − 1))

For x = 0 the property is obvious. For x <> 0 we prove the property by

algebraic manipulations.

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.12

Page 13: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.3. Construction methodology

3.3. Construction methodology

Objective

Construction of an SML program computing the function:

f(x) : D → R

given its specification S

Methodology

1. Case analysis

Recognize

• base case(s), and

• recursive case(s).

2. Partial correctness

Check that the base case returns correct result.

Show that the recursive case gives correct result, assuming that all

recursive calls give correct result.

3. Termination

Choose a variant.

A variant is an integer-valued expression on the input parameters

together with a lower bound b so that

• for any recursive call, the variant is ≥ b, and

• for any recursive call, the variant of the recursive call is strictly less

than the current call.

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.13

Page 14: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.4. Tips and hints

3.4. Tips and hints

Superfluous or overlapping cases

Try to keep your code short and compact. Avoid redundant cases!

fun fact n =

if n = 0 then 1

else if n = 1 then 1

else n ∗ fact (n−1)

Variants

Variants are usually simple; an integer given by a parameter or the size

of some input data. But watch out for the difficult cases!

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.14

Page 15: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

3.5. Forms of recursion

Up to now:

• One recursive call

• Some variant is decremented by one

That is: simple recursion

(construction process by simple induction)

Forms of recursion

• Simple recursion

• Complete recursion

• Multiple recursion

• Mutual recursion

• Nested recursion

• Recursion on a generalised problem

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.15

Page 16: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Complete recursion

Example 1: integer division (quotient and remainder)

Specification

function intDiv a b

TYPE: int → int → (int ∗ int)

PRE: a ≥ 0 and b > 0

POST: (q,r) such that a = q ∗ b + r and 0 ≤ r < b

Construction

Variant: a

Error case: a < 0 or b ≤ 0 : produce an error message

Base case: a < b : since a = 0 ∗ b + a, return (0,a)

This covers more than the minimal value of a (namely 0)!

General case: a ≥ b :

since a = q ∗ b + r iff (a−b) = (q−1) ∗ b + r,

the call intDiv (a−b) b will give q−1 and r

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.16

Page 17: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Program (intDiv.sml)

fun intDivAux a b =

if a < b then (0,a)

else let val (q1,r1) = intDivAux (a−b) b

in (q1+1,r1)

end

fun intDiv a b =

if a < 0 orelse b <= 0 then error "intDiv: invalid argument"

else intDivAux a b

Necessity of the induction hypothesis not only for a−1,

but actually for all values less than a: complete induction!

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.17

Page 18: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Example 2: exponentiation (revisited from Section 3.1)

Specification

function fastExpo x n

TYPE: real → int → real

PRE: n ≥ 0

POST: xn

Construction

Variant: n

Error case: n < 0: produce an error message

Base case: n = 0 : return 1

General case: n > 0 :

if n is even, then return xn div 2 ∗ xn div 2

otherwise, return x ∗ xn div 2 ∗ xn div 2

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.18

Page 19: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Program (expo.sml)

fun fastExpo x n =

let

fun fastExpoAux x 0 = 1.0

| fastExpoAux x n =

let val r = fastExpoAux x (n div 2)

in if even n then r ∗ r

else x ∗ r ∗ r

end

in

if n < 0 then error "fastExpo: negative argument"

else fastExpoAux x n

end

Complete recursion,

but the size of the input is divided by 2 each time!

Complexity

Let C(n) be the number of multiplications made

(in the worst case) by fastExpo:

C(0) = 0

C(n) = C(n div 2) + 2 for n > 0

One can show that C(n) = O(log n)

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.19

Page 20: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Multiple recursion

Example: the Fibonacci numbers

Definition

fib (0) = 1

fib (1) = 1

fib (n) = fib (n−1) + fib (n−2) for n > 1

Specification

function fib n

TYPE: int → int

PRE: n ≥ 0

POST: fib (n)

Program (fib.sml)

Variant: n

fun fib 0 = 1

| fib 1 = 1

| fib n = fib (n−1) + fib (n−2)

• Double recursion

• Inefficient: multiple recomputations of the same values!

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.20

Page 21: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Mutual recursion

Example: recognising even integers and odd integers

Specification

function even n

TYPE: int → bool

PRE: n ≥ 0

POST: true if n is even

false otherwise

function odd n

TYPE: int → bool

PRE: n ≥ 0

POST: true if n is odd

false otherwise

Program (even.sml)

Variant: n

fun even 0 = true

| even n = odd (n−1)

and odd 0 = false

| odd n = even (n−1)

• Simultaneous declaration of the functions

• Global correctness reasoning

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.21

Page 22: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Nested recursion

and lexicographic order

Example 1: the Ackermann function

Definition

For m, n ≥ 0:

acker (0,m) = m+1

acker (n,0) = acker (n−1, 1) for n > 0

acker (n,m) = acker (n−1, acker (n, m−1)) for n, m > 0

Program (acker.sml)

Variant: the pair (n,m)

fun acker 0 m = m+1

| acker n 0 = acker (n−1) 1

| acker n m = acker (n−1) (acker n (m−1))

where

(n′, m′) <lex (n, m) iff n′ < n or (n′ = n and m′ < m)

This is called a lexicographic order

• The function acker always terminates

• It is not a primitive-recursive function,

so it is impossible to estimate an upper bound for acker n m

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.22

Page 23: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Example 2: Graham’s number, the “largest” number

Definition

Operator ↑n (invented by Donald Knuth):

a ↑1 b = ab

a ↑n b = a ↑n−1 (b ↑n−1 b) for n > 1

Program (graham.sml)

Variant: n

fun opKnuth 1 a b = Math.pow (a,b)

| opKnuth n a b = opKnuth (n−1) a (opKnuth (n−1) b b)

- opKnuth 2 3.0 3.0 ;

val it = 7.62559748499E12 : real

- opKnuth 3 3.0 3.0 ;

! Uncaught exception: Overflow

Graham’s number is opKnuth 63 3.0 3.0

It is in the Guiness Book of Records!

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.23

Page 24: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Recursion on a generalised problem

Example: recognising prime numbers

Specification

function prime n

TYPE: int → bool

PRE: n > 0

POST: true if n is a prime number

false otherwise

Construction

It is impossible to determine whether n is prime

via the reply to the question “is n − 1 prime”?

It seems impossible to directly construct a recursive program

We thus need to find another function:

• that is more general than prime, in the sense

that prime is a particular case of this function

• for which a recursive program can be constructed

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.24

Page 25: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Specification of the generalised function

function divisors n low up

TYPE: int → int → int → bool

PRE: n, low, up ≥ 1

POST: true if n has no divisors in {low, . . . , up}false otherwise

Construction of a program for the generalised function

Variant: up − low + 1, which is the size of {low, . . . , up}

Base case: low > up :

return true because the set {low, . . . , up} is empty

General case: low ≤ up :

if n is divisible by low, then return false

otherwise, return whether n has a divisor in {low+1, . . . , up}

Construction of a program for the original function

The function prime is a particular case of the function divisors,

namely when low is 2 and up is n−1

One can also take up as ⌊√n⌋, and this is more efficient

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.25

Page 26: Chapter 3 Programming with Recursion - Uppsala  · PDF fileCh.3: Programming with Recursion Plan Chapter 3 Programming with Recursion 1. Examples ..... 3.2 2. Correctness

Ch.3: Programming with Recursion 3.5. Forms of recursion

Program (prime.sml)

fun divisors n low up =

low > up orelse

(n mod low) <> 0 andalso divisors n (low+1) up

fun prime n =

if n <= 0 then error "prime: non­positive argument"

else if n = 1 then false

else divisors n 2 (floor (Math.sqrt (real n)))

• The function divisors may be useful for other problems

• The discovery of divisors requires imagination and creativity

• There are some standard methods of generalising problems:

– let the recursive function take more parameters, so that the problem

we want to solve is a special case

– let the recursive function return more information than is required in

the problem statement

– tupling generalisation: replace a parameter by a list of parameters of

the same type

• Sometimes the only way to solve the problem at hand is to consider a

more general problem.

• Often, a generalized problem may be more efficient regarding time

and/or space consumption.

Exercises:

1. Which standard method of generalization did we apply in the

implementation of the primes function?

2. (Harder) Implement a function that computes fib(n) in time

proportional to n.

Hint: You will need to define a function that solves a more general

problem.

Sven-Olof Nystrom/IT Dept/Uppsala University FP 3.26