0% found this document useful (0 votes)
32 views47 pages

Programmazione Funzionale: Recursion

The document discusses recursion and provides examples of recursive functions. It defines a recursive function to calculate the factorial of a number. It explains that recursive functions are defined in terms of themselves, using a base case and step case. It then defines a recursive sum function to calculate the sum of integers from 1 to a given number n, using 0 as the base case and adding n to the recursive call of sum(n-1) as the step case.

Uploaded by

Andrea Dal Santo
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
32 views47 pages

Programmazione Funzionale: Recursion

The document discusses recursion and provides examples of recursive functions. It defines a recursive function to calculate the factorial of a number. It explains that recursive functions are defined in terms of themselves, using a base case and step case. It then defines a recursive sum function to calculate the sum of integers from 1 to a given number n, using 0 as the base case and adding n to the recursive call of sum(n-1) as the step case.

Uploaded by

Andrea Dal Santo
Copyright
© Attribution Non-Commercial (BY-NC)
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 47

Programmazione funzionale

Lecture 3
Recursion
PAQ (Previously Asked Questions)

• What is the difference between == and =?


(also between != and <>)
• Why does let v = expr not return a
boolean?
• Why is "bar" < "baz"?
• How are types inferred?

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| ≤ ε

• Where ε is a suitable accuracy


• An in-depth look at floating point arithmetic is
outside the scope of this course
6
PAQ: The let construct
• The let construct is a definition of a variable or
function
• Creates a binding from a name to a
value/function
• Definition does not have a value, but the
interpreter reports the value of whatever is
defined
– For variable definitions, the value of the variable
– For function definitions, the value of the function
– Definitions can not be used in expressions

7
Example
# let x = (4, 2);;
val x : int * int = (4, 2)

# let mean x y = (x +. y) /. 2.0;;


val mean : float > float > float = <fun>

The value is a
function

Functions are actually values too!


We will get back to that later in the course
8
PAQ: "bar" < "baz"
• O'Caml implements "dictionary" order on
strings
• This is called lexicographic order, and is
applicable to many other data structures
as well
• Short description (simplified):
1. Discard the common prefix of both strings
2. Compare the first character in each string

9
Example

"bar" < "baz"

b <
a
r = z
b
a

Common prefix: "ba"

But there are some corner cases, such as


"foo" < "foo" and "foo" < "foobar"

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

let x = 42;; + :int 3 :int

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

• expr1 must be of type bool


• expr2 and expr3 must be of the same type
• The type of the if-then-else is the same as
expr2 and 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

= :bool +.:float 0.0:float

'a':char 'A':char exp :float 1.0: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

3! = 2!×3 = 1!×2 × 3 = 0!×1× 2 × 3 = 1×1× 2 × 3 = 6

22
1 if n = 0
Example n!= 
(n − 1)!n otherwise

# let rec factorial n =


if n = 0
then 1
else n * (factorial (n-1));;
val factorial : int > int = <fun>
# factorial 3;;
 : int = 6

23
let rec factorial n =
if n = 0
Type inference then 1
else n * (factorial (n-1));;

if-then-else:int

=:bool 1:int *: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

• How can it be defined?

let rec sum n = ?

25
Step case
• Assuming that we know how to solve
sum (n-1), how can we compute sum n?

let rec sum n = n + sum (n-1);;

• This is called the step case

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

# let rec sumto x y = Step


case
if x > y Base
case
then 0
else x + sumto (x+1) y;;
val sumto : int > int > int = <fun>
# sumto 5 10;;
 : int = 45

Quiz: Can you think of another way to define the function?


29
Example
• Given sumto, we can redefine sum in an easier
way:

# let sum n = sumto 0 n;;


val sum : int > int = <fun>

• It’s no longer recursive!


• Often, defining the general case and then using
that is a good idea in a functional language
30
Questions?

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:

let id id* = expression1 in expression2

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);;

Matches any value,


binds it to x

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

let rec sum n =


match n with
0 -> 0
| _ -> n + sum (n-1);;

Matches any value,


does not bind it to a
variable 40
Pattern matching
• Pattern matching is a very powerful
construct, this is just a first taste
• We'll go into more detail later in the
course as we introduce lists and other
data structures

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:

# let rec fib n =


match n with
0 | 1 -> 1
| _ -> fib (n-1) + fib (n-2);;
val fib : int > int = <fun>
# fib 10;;
 : int = 89

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:

let id id* = expression1 in expression2

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

You might also like