Loading in 5 sec....

Function Definition by Cases and RecursionPowerPoint Presentation

Function Definition by Cases and Recursion

- 67 Views
- Uploaded on
- Presentation posted in: General

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

- 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.

Given the definition

x :: Int

x*x = 4

Is x equal to 2?

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.

- 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

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.

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

double :: Int -> Int

double x = x+x

double (3*5)

NO!

Haskell `remembers´

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

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.

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.

Note two equals signs, to

avoid confusion with a

definition.

2 <= 3True

2 > 3False

2 < 3True

2 == 3False

2 /= 3True

Not equals.

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.

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.

- 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

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.

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!

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

Edsger W. Dijkstra

(but it is very good at the latter).

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

- Helps us clarify what max is supposed to do.
- Can help in testing.
- Enables us to prove programs correct.

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.

QuickCheck is a tool to help you test your programs.

Main> quickCheck prop_Max

OK, passed 100 tests

quickCheck generates random values to test your property thoroughly.

- 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

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.

- 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.

- 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

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

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.

Base case.

fac :: Int -> Int

fac 0 = 1

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

Recursive case.

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

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.

- 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?

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 ^).

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

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

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?

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.

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!

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.

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.

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.

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.

- 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.