Upload
august-mills
View
214
Download
0
Embed Size (px)
Citation preview
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 -> Intdouble x = 2*x
double 8 2*8
16
Evaluation Order
double (3+5)
There may be more than one way to evaluate an expression:
double 8
2*(3+5)
2*8 16
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 -> Intdouble x = x+x
double (3*5)
double 15
15+15
(3*5)+(3*5)
15+(3*5)
30
Is it more work toevaluate the expression
in this order?
Sharing Evaluation
double :: Int -> Intdouble x = x+x
double (3*5)
double 15
15+15
(3*5)+(3*5)
30
NO!Haskell `remembers´that both occurrencesof 3*5 are really thesame, and evaluates
both in one step.
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
2 <= 3 True
2 > 3 False
2 < 3 True
2 == 3 False
2 /= 3 True
Note two equals signs, toavoid confusion with a
definition.
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 ifboth 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
OR
max :: Int -> Int -> Int
max x y | x <= y = y
| x > y = x
A guard: an expression oftype Bool.
If the guard is True,the equation applies.
Evaluation with GuardsTo 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 :: Int -> Int -> Intmax x y | x <= y = y | x > y = x
max 4 2 ?? 4 <= 2 False
?? 4 > 2 True
4
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
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.
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.
Quiz Answer
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)?
Then fac n = 1 * 2 * … * (n-1) * n
= fac (n-1) * n
A Table of Factorials
n fac n
0 1
1 1
2 2
3 6
4 24
...
Must start somewhere:we know that fac 0 = 1.
So fac 1 = 1 * 1.
So fac 2 = 1 * 2.
So fac 3 = 2 * 3.
A Recursive Definition of Factorial
fac :: Int -> Int
fac 0 = 1
fac n | n > 0 = fac (n-1) * n
Base case.
Recursive case.
Evaluating Factorials
fac :: Int -> Int
fac 0 = 1
fac n | n > 0 = fac (n-1) * n
fac 4 ?? 4 == 0 False
?? 4 > 0 True
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 4 div (fac 5) 5
div (div (fac 6) 6) 5
div (div (div (fac 7) 7) 6) 5
...
A true statement.
Not a usefuldefinition.
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
power x 0 = 1
power x n | n > 0 = power x (n-1) * x
Don’t forget the base case!
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
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
Base case is still needed.
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
6 function calls, 5 multiplications.
4 function calls,4 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 means No k in the range 2<=k<ndivides n.
GeneralisationReplace 2 by a variable.
Define
factors m n == True if Some k in the range m<=k<ndivides n.
So prime 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
if m divides n,
or some 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 therange 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.