1 / 43

# Function Definition by Cases and Recursion - PowerPoint PPT Presentation

Function Definition by Cases and Recursion. Lecture 2, Programmeringsteknik del A. Definitions Revisited. A definition double :: Int -> Int double x = 2*x makes a true statement about the function defined, (whatever x is, then double x and 2*x are equal)

I am the owner, or an agent authorized to act on behalf of the owner, of the copyrighted work described.

Function Definition by Cases and Recursion

Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author.While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server.

- - - - - - - - - - - - - - - - - - - - - - - - - - E N D - - - - - - - - - - - - - - - - - - - - - - - - - -

## Function Definition by Cases and Recursion

Lecture 2,

Programmeringsteknik del A

### Definitions Revisited

• A definition

• double :: Int -> Int

• double x = 2*x

• makes a true statement about the function defined,

• (whatever x is, then double x and 2*x are equal)

• gives a way of computing calls of the function.

### Quiz

Given the definition

x :: Int

x*x = 4

Is x equal to 2?

### Quiz

Given the definition

x :: Int

x*x = 4

Is x equal to 2?

NO! This is not a valid Haskell definition.

It makes a true statement about x, but it does not

give a way of computing x.

### Computing with Definitions

• A function call is computed by

• replacing the call with a copy of the right hand side,

• with the argument names replaced by the actual arguments.

double :: Int -> Int

double x = 2*x

double 82*8

16

### Evaluation Order

There may be more than one way to evaluate an expression:

double 8

double (3+5)

2*8

16

2*(3+5)

You can use any order of evaluation; they all give the same result. Haskell chooses a suitable one; you don’t need to know which.

### Sharing Evaluation

double :: Int -> Int

double x = x+x

double (3*5)

Is it more work to

evaluate the expression

in this order?

(3*5)+(3*5)

double 15

15+(3*5)

15+15

30

### Sharing Evaluation

double :: Int -> Int

double x = x+x

double (3*5)

NO!

that both occurrences

of 3*5 are really the

same, and evaluates

both in one step.

(3*5)+(3*5)

double 15

15+15

30

### Definition by Cases

Often programs must make decisions, and compute different results in different cases.

Example:Define max x y to return the maximum of its

two arguments.

If x <= y, then max x y should be y.

If x>y, then max x y should be x.

### The Type of Booleans

We make a decision by asking: does a condition hold?

(e.g. Does x<=y hold?)

A condition is either true or false: this is a piece of data, a value!

We introduce a new basic type with two values, named after the mathematician George Boole:

True, False :: Bool

Constants begin with a capital letter.

### Some Operators Producing Booleans

Note two equals signs, to

avoid confusion with a

definition.

2 <= 3True

2 > 3False

2 < 3True

2 == 3False

2 /= 3True

Not equals.

### Functions Returning Booleans

Functions can return boolean results (or any other type).

Example:

inOrder :: Int -> Int -> Int -> Bool

inOrder x y z = x <= y && y <= z

a && b is True if

both a and b are True.

### Using Booleans to Define Functions by Cases

max :: Int -> Int -> Int

max x y | x <= y = y

max x y | x > y = x

max :: Int -> Int -> Int

max x y | x <= y = y

| x > y = x

OR

A guard: an expression of

type Bool.

If the guard is True,

the equation applies.

### Evaluation with Guards

• To evaluate a function call,

• evaluate each guard in turn until one is True,

• replace the call with a copy of the right hand side following the true guard.

max 4 2 ?? 4 <= 2False

?? 4 > 2True

4

max :: Int -> Int -> Int

max x y | x <= y = y

| x > y = x

### Is max Correct?

Programming is a very error prone process; programs are rarely correct `first time´.

A large part of the cost of software development goes on finding and correcting errors.

It is essential to test software: try it on a variety of inputs and see if the output is correct.

### Choosing Test Data

Test data should be chosen carefully, to include `difficult´ cases that might induce a failure.

The max function should be tested at least with x<y, x==y, x>y, and probably combinations of positive and negative arguments.

Choose enough test examples so that every case in your program is used at least once!

### Dijkstra on Testing

”Testing can never demonstrate the absence of errors in software, only their presence”

Edsger W. Dijkstra

(but it is very good at the latter).

### Specifications

What do we mean by `max is correct´?

A specification formulates properties we expect max to satisfy.

Property:x <= max x y

Property:y <= max x y

### Why Formulate Specifications?

• Helps us clarify what max is supposed to do.

• Can help in testing.

• Enables us to prove programs correct.

### Specifications and Testing

We can define function to check whether properties hold.

prop_Max :: Int -> Int -> Bool

prop_Max x y = x <= max x y && y <= max x y

If prop_Max always returns True, then the specification is satisfied.

We can test max on many inputs without needing to inspect the results by hand.

### Testing with QuickCheck

Main> quickCheck prop_Max

OK, passed 100 tests

quickCheck generates random values to test your property thoroughly.

### Testing with QuickCheck (2)

• What if we make a mistake?

• max x y | x <= y = x

• | x > y = y

Main> quickCheck prop_Max

Falsifiable, after 0 tests

1

0

### Specifications and Proofs

From the definition of max:

x <= y==> max x y = y

x > y==> max x y = x

Theorem: x <= max x y

Proof:Consider two cases:

Case x <= y:y = max x y, so x <= max x y.

Case x > y:max x y = x and x <= x, so x <= max x y.

### Formal Methods

• Proofs are costly and also error-prone, but can guarantee correctness.

• Thorough testing is the most common method today.

• Customers for safety critical software demand proofs today.

• Proofs of correctness will play a growing role, thanks to

• automatic tools to help with proving,

• demand for better quality software.

### Quiz

• Define

• abs x to return the absolute value of x (e.g. abs 2 = 2, abs (-3) = 3.

• sign x to return 1 if x is positive, and -1 if x is negative.

• State (and prove?) a property relating abs and sign.

abs x | x <= 0 = -x

| x > 0 = x

sign x | x < 0 = -1

| x > 0 = 1

| x == 0 = 0

Property: x == sign x * abs x

Did you consider this case?

This can also be written

sign 0 = 0

### Recursion

Problem:define fac :: Int -> Int

fac n = 1 * 2 * … * n

What if we already know the value of fac (n-1)?

Thenfac n= 1 * 2 * … * (n-1) * n

= fac (n-1) * n

### A Table of Factorials

Must start somewhere:

we know that fac 0 = 1.

nfac n

01

11

22

36

424

...

So fac 1 = 1 * 1.

So fac 2 = 1 * 2.

So fac 3 = 2 * 3.

### A Recursive Definition of Factorial

Base case.

fac :: Int -> Int

fac 0 = 1

fac n | n > 0 = fac (n-1) * n

Recursive case.

### Evaluating Factorials

fac :: Int -> Int

fac 0 = 1

fac n | n > 0 = fac (n-1) * n

fac 4 ?? 4 == 0False

?? 4 > 0True

fac (4-1) * 4

fac 3 * 4

fac 2 * 3 * 4

fac 1 * 2 * 3 * 4

fac 0 * 1 * 2 * 3 * 4

1 * 1 * 2 * 3 * 4

24

### There is No Magic!

What if we define

fac :: Int -> Int

fac n = div (fac (n+1)) (n+1) ?

fac 4div (fac 5) 5

div (div (fac 6) 6) 5

div (div (div (fac 7) 7) 6) 5

...

A true statement.

Not a useful

definition.

### Primitive Recursion

• Define

• f n in terms of f (n-1), for n > 0.

• f 0 separately.

• What if I already know the value of f (n-1)?

• Can I compute f n from it?

### Quiz

Define a function power so that

power x n == x * x * … * x

n times

(Of course, power x n == x^n, but you should define power without using ^).

### Quiz

Define a function power so that

power x n == x * x * … * x

n times

Don’t forget the base case!

power x 0 = 1

power x n | n > 0 = power x (n-1) * x

Since this equals

(x * x * … * x) * x

n-1 times

### General Recursion

What if I know the values of f x for all x less than n?

Can I compute f n from them?

Example

x^(2*n) == (x*x)^n

x^(2*n+1) == (x*x)^n * x

### Power Using General Recursion

Base case is still needed.

power :: Int -> Int -> Int

power x 0 = 1

power x n

| n `mod` 2 == 0= power (x*x) (n `div` 2)

| n `mod` 2 == 1= power (x*x) (n `div` 2) * x

Two recursive cases.

Why might this definition of power be preferred?

### Comparing the Versions

First Version

power 3 5

power 3 4 * 3

power 3 3 * 3 * 3

power 3 2 * 3 * 3 * 3

power 3 1 * 3 * 3 * 3 * 3

power 3 0 * 3 * 3 * 3 * 3 * 3

1 * 3 * 3 * 3 * 3 * 3

243

Second Version

power 3 5

power 9 2 * 3

power 81 1 * 3

power 81 0 * 81 * 3

1 * 81 * 3

243

4 function calls,

4 multiplications.

6 function calls,

5 multiplications.

### A More Difficult Example

Define prime :: Int -> Bool, so that prime n is True if n is a prime number.

What if we know whether (n-1) is prime?

What if we know whether each smaller number is prime?

NO HELP!

### Generalise the Problem!

n is prime meansNo k in the range 2<=k<n

divides n.

Generalisation

Replace 2 by a variable.

Define

factors m n == True if Some k in the range m<=k<n

divides n.

Soprime n = not (factors 2 n)

not x is True if x is False,

and vice versa.

### Recursive Decomposition

Problem: Does any k in the range m<=k<n divide n?

What if we know whether any k in a smaller range divides n?

Some k in the range m<=k<n divides n

ifm divides n,

orsome k in the range m+1<=k<n divides n.

### Recursive Solution

factors :: Int -> Int -> Bool

factors m n

| m == n = False

| m < n = divides m n || factors (m+1) n

divides :: Int -> Int -> Bool

divides m n = n `mod` m == 0

There is no k in the

range n<=k<n.

x || y is True

if x is True or y is True.

### What is Getting Smaller?

The range m<=k<n contains n-m elements. Call this the problem size.

factors m n

| m == n = False

| m < n = divides m n || factors (m+1) n

Base case: n-m == 0

Recursive case: n-(m+1) == (n-m)-1

The problem size gets smaller in each call, until it reaches zero. So recursion terminates.

### Lessons

• Recursion lets us decompose a problem into smaller subproblems of the same kind -- a powerful problem solving tool in any programming language!

• A more general problem may be easier to solve recursively than a `simpler´ one, because the recursive calls can do more.

• To ensure termination, define a `problem size´ which must be greater than zero in the recursive cases, and decreases by at least one in each recursive call.