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 indexed with the name “nat” (the name of the type of natural numbers, i.e., non-negative integers here):
let fold_right_nat zero_case succ_case n_given =
(* fold_right_nat : '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 fold_right_nat:
let twice_v4 n =
fold_right_nat 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 =
fold_right_nat 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 fold_right_nat:
let add_v3 x y =
fold_right_nat 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 =
fold_right_nat 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 =
fold_right_nat z succ;;
Harald: Harumph.
The inductive specification of mul read as follows, given an integer y:
This specification suggests that we could implement mul as an instance of fold_right_nat:
let mul_v5 x y =
fold_right_nat 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 fold_right_nat.
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 fold_right_nat:
let evenp_v2 n =
fold_right_nat 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 =
fold_right_nat true not n;;
Vigfus: Simpler too:
let evenp_v4 =
fold_right_nat 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 fold_right_nat.