The goal of this lecture note is to explore the pattern of structural recursion associated to natural numbers.
Overall, for a variety of examples (twice, add, mul, power, evenp, oddp), we have specified a computation by mathematical induction, and then we have implemented the corresponding function in OCaml by structural recursion. In each case, we started from the following skeleton:
let ... n_given =
let () = assert (n_given >= 0) in
let rec visit n =
if n = 0
then ...
else let n' = n - 1
in let ih = visit n'
in ...
in visit n_given;;
We then proceeded by
Let us abstract this skeleton into an OCaml function, traditionally called “fold_right” (down the road, we will meet its cousin “fold_left”) and pre-indexed with the name “nat” (the name of the type of natural numbers, i.e., non-negative integers here):
let nat_fold_right zero_case succ_case n_given =
(* nat_fold_right : 'a -> ('a -> 'a) -> int -> 'a *)
let () = assert (n_given >= 0) in
let rec visit n =
if n = 0
then zero_case
else let n' = n - 1
in let ih = visit n'
in succ_case ih
in visit n_given;;
(Incidentally, in the induction step, the let-expressions that declare n' and ih, in the definition of visit, are cosmetic and so we could unfold them.)
The inductive specification of twice read as follows:
This specification suggests that we could implement twice as an instance of nat_fold_right:
let twice_v4 n =
nat_fold_right 0 (fun ih -> succ (succ ih)) n;;
This implementation passes the unit test:
# test_twice twice_v4;;
- : bool = true
#
Harald: This definition can be simplified.
Alfrothul: Indeed it can:
let twice_v5 =
nat_fold_right 0 (fun ih -> succ (succ ih));;
Harald: And it passes the unit test too:
# test_twice twice_v5;;
- : bool = true
#
The inductive specification of add read as follows, given an integer y:
This specification suggests that we could implement add as an instance of nat_fold_right:
let add_v3 x y =
nat_fold_right y (fun ih -> succ ih) x;;
This implementation passes the unit test:
# test_add add_v3;;
- : bool = true
#
Vigfus: Hehe, even shorter:
let add_v4 x y =
nat_fold_right y succ x;;
Halcyon: And it passes the unit test too:
# test_add add_v4;;
- : bool = true
#
Harald: We however can’t simplify it, since x is not the last formal parameter.
Alfrothul: Well, it could be. The addition function is commutative, right?
Vigfus: I guess it is:
let add_v5 z =
nat_fold_right z succ;;
Harald: Harrumph.
The inductive specification of mul read as follows, given an integer y:
This specification suggests that we could implement mul as an instance of nat_fold_right:
let mul_v5 x y =
nat_fold_right 0 (fun ih -> y + ih) x;;
This implementation passes the unit test:
# test_mul mul_v5;;
- : bool = true
#
The inductive specification of exponentiation read as follows, given an integer x:
Along the lines of the previous sections, implement the exponentiation function as an instance of nat_fold_right.
The inductive specification of how to test whether a natural number is even read as follows:
This specification suggests that we could implement evenp as an instance of nat_fold_right:
let evenp_v2 n =
nat_fold_right true (fun ih -> not ih) n;;
This implementation passes the unit test:
# test_evenp evenp_v2;;
- : bool = true
#
Halcyon: Hehe, even shorter:
let evenp_v3 n =
nat_fold_right true not n;;
Vigfus: Simpler too:
let evenp_v4 =
nat_fold_right true not;;
The goal of this section is to write a predicate computing whether a given natural number is odd.
Along the lines of the previous section, implement the oddp predicate as an instance of nat_fold_right.
Fixed a typo in the section revisiting the exponentiation of two integers, thanks to Chiau Ren Yu’s eagle eye [20 Feb 2021]
Created [20 Feb 2021]