Thinking Recursively: COS 326 David Walker Princeton University
Thinking Recursively: COS 326 David Walker Princeton University
COS 326
David Walker
Princeton University
An example:
2 :: 3 :: 5 :: [ ] has type int list
is the same as: 2 :: (3 :: (5 :: [ ]))
"::" is called "cons"
Typing Lists
Typing rules for lists:
(1)
(2)
if e1 : t and e2 : t list
then e1 :: e2 : t list
Typing Lists
Typing rules for lists:
(1)
(2)
if e1 : t and e2 : t list
then e1 :: e2 : t list
More examples:
(1 + 2) :: (3 + 4) :: [ ]
: ??
(2 :: [ ]) :: (5 :: 6 :: [ ]) :: [ ]
: ??
[ [2]; [5; 6] ]
: ??
Typing Lists
Typing rules for lists:
(1)
(2)
if e1 : t and e2 : t list
then e1 :: e2 : t list
More examples:
(1 + 2) :: (3 + 4) :: [ ]
: int list
(2 :: [ ]) :: (5 :: 6 :: [ ]) :: [ ]
[ [2]; [5; 6] ]
Another Example
What type does this have?
[ 2 ] :: [ 3 ]
Another Example
What type does this have?
[ 2 ] :: [ 3 ]
int list
int list
rule: e1 :: e2 : t list
if
e1 : t
and
e2 : t list
# [2] :: [3];;
Error: This expression has type int but an
expression was expected of type
int list
#
Another Example
What type does this have?
[ 2 ] :: [ 3 ]
int list
int list
Another Example
What type does this have?
[ 2 ] :: [ 3 ]
int list
int list
2 :: [ 3 ]
[ 2 ] :: [ [ 3 ] ]
: int list
: int list list
Analyzing Lists
Just like options, there are two possibilities when
deconstructing lists. Hence we use a match with two branches
;;
Analyzing Lists
Just like options, there are two possibilities when
deconstructing lists. Hence we use a match with two branches
Analyzing Lists
Just like options, there are two possibilities when
deconstructing lists. Hence we use a match with two branches
;;
(Give it a try.)
;;
;;
;;
;;
is this ok?
;;
Clean up.
Reorganize the cases.
Pattern matching proceeds in order.
Characters 39-78:
..match xs with
x :: xs -> x + sum xs..
Warning 8: this pattern-matching is not exhaustive.
Here is an example of a value that is not matched:
[]
val sum : int list -> int = <fun>
INSERTION SORT
-2
sorted
-4
10
unsorted
-2
-4
10
unsorted
sorted
At each step, take the next item in the array and insert it in
order into the sorted portion of the list
-5
-4
sorted
-2
10
unsorted
list 2:
list 1:
-5
-2
-4
10
sorted
unsorted
Insert
;;
Insert
;;
Insert
;;
a familiar pattern:
analyze the list by cases
Insert
;;
Insert
Insert
Insertion Sort
type il = int list
insert : int -> il -> il
(* insertion sort *)
let rec insert_sort(xs : il) : il =
;;
Insertion Sort
type il = int list
insert : int -> il -> il
(* insertion sort *)
let rec insert_sort(xs : il) : il =
let rec aux (sorted : il) (unsorted : il) : il =
in
;;
Insertion Sort
type il = int list
insert : int -> il -> il
(* insertion sort *)
let rec insert_sort(xs : il) : il =
let rec aux (sorted : il) (unsorted : il) : il =
in
aux [] xs
;;
Insertion Sort
type il = int list
insert : int -> il -> il
(* insertion sort *)
let rec insert_sort(xs : il) : il =
let rec aux (sorted : il) (unsorted : il) : il =
match unsorted with
| [] ->
| hd :: tl ->
in
aux [] xs
;;
Insertion Sort
type il = int list
insert : int -> il -> il
(* insertion sort *)
let rec insert_sort(xs : il) : il =
let rec aux (sorted : il) (unsorted : il) : il =
match unsorted with
| [] -> sorted
| hd :: tl -> aux (insert hd sorted) tl
in
aux [] xs
;;
Some examples:
let
let
let
let
l0
l1
l2
l3
=
=
=
=
[];;
1::l0;;
2::l1;;
3::l2;;
(*
(*
(*
(*
length
length
length
length
is
is
is
is
0
1
2
3
*)
*)
*)
*)
A Loopy Program
let loop (xs : int list) : int =
match xs with
[] -> []
| hd::tail -> hd + loop (0::tail)
;;
A Loopy Program
let loop (xs : int list) : int =
match xs with
[] -> []
| hd::tail -> hd + loop (0::tail)
;;
Does this program terminate? No! Why not? We call loop recursively on (0::tail).
This list is the same size as the original list -- not smaller.
Take-home Message
ML has a strong type system
ML types say a lot about the set of values that inhabit them
In this case, the tail of the list is always shorter than the whole list
This makes it easy to write functions that terminate; it would be
harder if you had to consider more cases, such as the case that the
tail of a list might loop back on itself
Note: Just because the list type excludes cyclic structures does not
mean that an ML program can't build a cyclic data structure if it
wants to. (We'll do that later in the course.)
xkcd
PROGRAMMING WITH
NATURAL NUMBERS
Natural Numbers
Natural numbers are a lot like lists
both can be defined recursively (inductively)
An Example
(* precondition: n is a natural number
return double the input *)
let rec double_nat (n : int) : int =
;;
By definition of naturals:
n = 0 or
n = m+1 for some nat m
An Example
(* precondition: n is a natural number
return double the input *)
let rec double_nat (n : int) : int =
match n with
| 0 ->
| _ ->
;;
two cases:
one for 0
one for m+1
By definition of naturals:
n = 0 or
n = m+1 for some nat m
An Example
(* precondition: n is a natural number
return double the input *)
let rec double_nat (n : int) : int =
match n with
| 0 -> 0
| _ ->
;;
solve easy base case first
consider:
what number is double 0?
By definition of naturals:
n = 0 or
n = m+1 for some nat m
An Example
(* precondition: n is a natural number
return double the input *)
let rec double_nat (n : int) : int =
match n with
| 0 -> 0
| _ -> ????
;;
assume double_nat m is correct
where n = m+1
thats the inductive hypothesis
By definition of naturals:
n = 0 or
n = m+1 for some nat m
An Example
(* precondition: n is a natural number
return double the input *)
let rec double_nat (n : int) : int =
match n with
| 0 -> 0
| _ -> 2 + double_nat (n-1)
;;
assume double_nat m is correct
where n = m+1
thats the inductive hypothesis
By definition of naturals:
n = 0 or
n = m+1 for some nat m
An Example
(* fail if the input is negative
double the input if it is non-negative *)
let double (n : int) : int =
let rec double_nat (n : int) : int =
match n with
0 -> 0
| n -> 2 + double_nat (n-1)
in
nest double_nat so it
can only be called by
double
raises exception
if n < 0 then
failwith "negative input!"
else
double_nat n
;;
unary decomposition
A natural n is either:
0,
1,
m+2, where m is a natural
A natural n is either:
0,
m*2
m*2+1
binary decomposition
unary decomposition
A list xs is either:
[],
[x],
x::y::ys, where ys is a list
A natural n is either:
0,
m*2
m*2+1
Summary
Instead of while or for loops, functional programmers use
recursive functions
These functions operate by:
We've seen:
lists with cases:
(1) empty list, (2) a list with one or more elements
END