Programmazione Funzionale: Recursion
Programmazione Funzionale: Recursion
Lecture 3
Recursion
PAQ (Previously Asked Questions)
2
PAQ: == vs =
• Structural equality
– Compares two values for equality
• Physical equality
– Compares two values for identity
– c.f. comparison on pointer values for
dynamically allocated objects in C++
• For some types physical and structural
equality is the same (int, bool, char)
3
Example
# (1, 2) = (1, 2);; (1,2) (1,2)
: bool = true
# (1, 2) == (1, 2);; (1,2) (1,2)
: bool = false
# let x = (2, 2);; x (2,2)
val x : int * int = (2, 2)
# x = x;;
: bool = true
# x == x;;
: bool = true
4
= or ==?
• Structural equality is usually the desired
semantics (but can be slower than testing
physical equality)
• Physical equality is always fast (constant
time) but needs more care to be used
correctly
• Recommendation: When in doubt, use
structural equality
5
Floating point comparison
• One should almost never compare floating point
values using =,<> (or ==,!=)
• Computations introduce errors in the result
• Therefore one usually tests for equality between
x and y with (simplified)
| x− y| ≤ ε
7
Example
# let x = (4, 2);;
val x : int * int = (4, 2)
The value is a
function
9
Example
b <
a
r = z
b
a
10
PAQ: Type inference
• Figuring out the type of an expression of
definition is called type inference
• Given an O'Caml snippet, the interpreter
will
– Infer the type (and check that it is consistent)
– Evaluate the expression/Add the definition
• But how does it perform the first step?
11
Abstract syntax trees
• Any expression or definition can be
represented as a tree
• Example: (x+7)*3 can be represented as
*
+ 3
x 7
• This is called an abstract syntax tree
12
Type inference
• First we build the abstract syntax tree
• Then, we iterate through the nodes depth
first (bottom up) and label each node with
its type:
* :int
• Assume x is defined as
x:int 7:int
13
Type inference
• For each node, we also check that the
children have the correct type
• Example: (7 + 1.0)*3
*
Type clash
+ :int 3
7 :int 1.0:float
14
if-then-else
• For if-then-else expressions
if expr1 then expr2 else expr3
15
Variable definitions
• For variable definitions, we construct the
abstract syntax tree for the right hand side and
perform type inference as before
• The type of the root node will be the type of the
variable
• Example
let x =
if 'a' < 'A'
then exp(1.0) +. 1.0
else 0.0;;
16
let x =
if 'a' < 'A'
Example then exp(1.0) +. 1.0
else 0.0;;
if-then-else:float
1.0 :float
17
Function definitions
• For functions it gets a little more
complicated
• We now need to infer the type of
– All arguments
– The result
• The basic idea is to proceed in the same
way as for variable definitions, but at the
same time infer the type of the arguments
18
Example
let f x y =
if x = 0 then y else y +. float_of_int x
x : int
?
a
if-then-else :float y : float
?
a
f : int
?→?
a →→float
?→
a ? ? → ?float
= :bool y :float
:a +.:float
x :a
:int 0 :int y :a
:float float_of_int:float
x :int 19
Further reading
• Many of the gory details on type inference
and much more are available in
Didier Rémy, “Using, Understanding, and
Unraveling The O'Caml Language: From
Practice to Theory and vice versa”
http://pauillac.inria.fr/~remy/cours/appsem/
• (but this is an advanced text)
20
PAQ
• We'll be happy to adapt the contents of
the course based on your questions or
suggestions
• So ask away!
21
Recursion
• Many functions in mathematics are
defined in terms of themselves:
• Example
1 if n = 0
n!=
(n − 1)!n otherwise
22
1 if n = 0
Example n!=
(n − 1)!n otherwise
23
let rec factorial n =
if n = 0
Type inference then 1
else n * (factorial (n-1));;
if-then-else:int
n :int
:a 0:int n:int factorial:a
:int
- :int
n : int
?
a
factorial : int
a→?
? ? int
→ ?
a
n:int 1:int
24
Structure of recursive functions
• Write a recursive function computing
i=n
sum(n) = ∑ i
i =1
25
Step case
• Assuming that we know how to solve
sum (n-1), how can we compute sum n?
26
Base case
• This function does not quite work:
sum 2 = 2 + sum 1 = 2 + 1 + sum 0 =
2 + 1 + 0 = sum -1 = 2 + 1 + 0 + -1 + sum -2 = …
• We need to stop somewhere… We need a
base case
• If n = 0, we already know the answer:
sum 0 = 0
27
Putting it together
# let rec sum n =
if n = 0
then 0
else n + sum (n-1);;
val sum : int > int = <fun>
# sum 10;;
: int = 55
c.f. proof by induction.
How would you prove
that this function
computes the sum?
28
i =n
Example sumto(m, n) = ∑ i
i =m
31
Example: Fibonacci
# let rec fib n =
if n = 0 or n = 1
then 1
else fib (n-1) + fib (n-2);;
val fib : int > int = <fun>
# fib 10;;
: int = 89
32
Example: Fibonacci
• But this implementation is not very
efficient:
# fib 10;;
: int = 89
# fib 100;;
: int = 277887173
33
Example: Fibonacci
# let rec fib' a b n =
if n = 0 then a
else if n = 1 then b
else fib' b (a+b) (n-1);;
val fib' : int > int = <fun>
# let fib n = fib' 1 1 n;;
val fib : int > int = <fun>
# fib 100;;
: int = 277887173
34
Local definitions
• The function fib' is only used by fib
• We can make it a local definition
• Syntax:
35
Local definitions
# let fib n =
let rec fib' a b n =
if n = 0 then a
else if n = 1 then b
else fib' b (a+b) (n-1)
in fib' 1 1 n;;
val fib : int > int = <fun>
36
37
Pattern matching
• Pattern matching is a construct somewhat
similar to the switch statement of C++
• Syntax
match expr with
pattern1 -> expr1
| pattern2 -> expr2
| pattern3 -> expr3
…
• It matches not just against explicit values
like switch in C++, but against patterns
38
Pattern matching
let rec sum n =
match n with
0 -> 0
| x -> x + sum (x-1);;
39
Pattern matching
• Often, we don't need to bind the pattern to a value
• We can use the special symbol _ to denote that we'd like
to match against anything without giving it a name
41
Example: Fibonacci
# let rec fib n =
match n with
0 -> 1
| 1 -> 1
| _ -> fib (n-1) + fib (n-2);;
val fib : int > int = <fun>
# fib 10;;
: int = 89
42
Example: Fibonacci
• When many patterns have the same expression
they can be merged:
43
Example: Fibonacci
• But this implementation is not very
efficient:
# fib 10;;
: int = 89
# fib 100;;
: int = 277887173
44
Example: Fibonacci
# let rec fib' a b n =
match n with
0 -> a
| 1 -> b
| _ -> fib' b (a+b) (n-1);;
val fib' : int > int = <fun>
# let fib n = fib' 1 1 n;;
val fib : int > int = <fun>
# fib 100;;
: int = 277887173
45
Local definitions
• The function fib' is only used by fib
• We can make it a local definition
• Syntax:
46
Local definitions
# let fib n =
let rec fib' a b n =
match n with
0 -> a
| 1 -> b
| _ -> fib' b (a+b) (n-1)
in fib' 1 1 n;;
val fib : int > int = <fun>
47