Example: Natural Number

我们可以定义一个递归变体,它的行为类似于数字,从而证明我们实际上并不一定需要将数字内置到 OCaml 中!(不过出于效率考虑,将数字内置进去是件好事。)

自然数要么是零,要么是另一个自然数的后继。这是你在数理逻辑课程中可能会定义自然数的方式,它自然地导致以下 OCaml 类型 nat

type nat = Zero | Succ of nat
type nat = Zero | Succ of nat

我们已经定义了一个新类型 natZeroSucc 是这种类型值的构造函数。这使我们能够构建具有任意数量嵌套 Succ 构造函数的表达式。这样的值就像自然数:

let zero = Zero
let one = Succ zero
let two = Succ one
let three = Succ two
let four = Succ three
val zero : nat = Zero
val one : nat = Succ Zero
val two : nat = Succ (Succ Zero)
val three : nat = Succ (Succ (Succ Zero))
val four : nat = Succ (Succ (Succ (Succ Zero)))

现在我们可以编写函数来操作这种类型的值。在下面的代码中,我们将写很多类型注释,以帮助读者跟踪哪些值是 nat 而不是 int ;当然,编译器不需要我们的帮助。

let iszero = function
  | Zero -> true
  | Succ _ -> false

let pred = function
  | Zero -> failwith "pred Zero is undefined"
  | Succ m -> m
val iszero : nat -> bool = <fun>
val pred : nat -> nat = <fun>

同样地,我们可以定义一个函数来相加两个数字:

let rec add n1 n2 =
  match n1 with
  | Zero -> n2
  | Succ pred_n -> add pred_n (Succ n2)
val add : nat -> nat -> nat = <fun>

我们可以将 nat 值转换为类型 int ,反之亦然:

let rec int_of_nat = function
  | Zero -> 0
  | Succ m -> 1 + int_of_nat m

let rec nat_of_int = function
  | i when i = 0 -> Zero
  | i when i > 0 -> Succ (nat_of_int (i - 1))
  | _ -> failwith "nat_of_int is undefined on negative ints"
val int_of_nat : nat -> int = <fun>
val nat_of_int : int -> nat = <fun>

确定一个自然数是偶数还是奇数,我们可以编写一对相互递归的函数:

let rec even = function Zero -> true | Succ m -> odd m
and odd = function Zero -> false | Succ m -> even m
val even : nat -> bool = <fun>
val odd : nat -> bool = <fun>