在开始本章之前,我们需要从上一章中导入我们的所有定义:
From Coq Require Export String.
Theorem add_comm : forall n m : nat,
n + m = m + n.
Proof.
induction n as [| n' IHn'].
- induction m as [| m' IHm'].
-- simpl. reflexivity.
-- simpl. rewrite <- IHm'. simpl. reflexivity.
- induction m as [| m' IHm'].
-- simpl. rewrite -> IHn'. simpl. reflexivity.
-- simpl. rewrite <- IHm'. rewrite -> IHn'. simpl. rewrite -> IHn'. reflexivity.
Qed.
数对 Pairs of Numbers
在归纳类型定义中,每个构造函数可以接受任意数量的参数——无(如true和O)、一个(如S)或多个(如Nyble,此处):
Inductive natprod
Inductive natprod : Type :=
| pair (n1 n2 : nat).
该声明可以理解为:“构造一对数字的唯一方法是将构造函数对应用于nat类型的两个参数。”
Check (pair 3 5) : natprod.
下面是用于提取一对的第一和第二分量的简单函数。
Definition fst
Definition fst (p : natprod) : nat :=
match p with
| pair x y ⇒ x
end.
Definition snd
Definition snd (p : natprod) : nat :=
match p with
| pair x y ⇒ y
end.
Compute (fst (pair 3 5)).
(* ===> 3 *)
由于在下面的内容中将大量使用对,因此能够使用标准数学符号(x,y)而不是 pair x y 来编写它们是很好的。我们可以告诉Coq允许使用符号声明。
Notation "( x , y )"
Notation "( x , y )" := (pair x y).
新符号既可以用于表达式,也可以用于模式匹配。
Compute (fst (3,5)).
Definition fst'
Definition fst' (p : natprod) : nat :=
match p with
| (x,y) ⇒ x
end.
Definition snd'
Definition snd' (p : natprod) : nat :=
match p with
| (x,y) ⇒ y
end.
Definition swap_pair
Definition swap_pair (p : natprod) : natprod :=
match p with
| (x,y) ⇒ (y,x)
end.
请注意,配对上的模式匹配(带括号:(x,y))不能与我们前面看到的“多模式”语法(不带括号:x,y)混淆。上面的示例说明了对元素x和y的模式匹配,而例如,“基本”中的减号定义对值n和m执行模式匹配:
Fixpoint minus (n m : nat) : nat :=
match n, m with
| O , _ ⇒ O
| S _ , O ⇒ n
| S n', S m' ⇒ minus n' m'
end.
区别很小,但值得知道的是,它们并不相同。例如,以下定义格式不正确:
Definition bad_fst
(* 一对多模式不能匹配: *)
Definition bad_fst (p : natprod) : nat :=
match p with
| x, y ⇒ x
end.
Definition bad_minus
(* 无法匹配具有成对模式的多个值: *)
Definition bad_minus (n m : nat) : nat :=
match n, m with
| (O , _ ) ⇒ O
| (S _ , O ) ⇒ n
| (S n', S m') ⇒ bad_minus n' m'
end.
现在,让我们来证明一些关于配对的简单事实。
如果我们以一种稍微奇特的方式陈述对的性质,我们有时可以用自反性(及其内置的简化)来完成它们的证明:
Theorem surjective_pairing'
Theorem surjective_pairing' : ∀ (n m : nat),
(n,m) = (fst (n,m), snd (n,m)).
Proof.
reflexivity. Qed.
但是,如果我们以更自然的方式陈述引理,那么自反性是不够的:
Theorem surjective_pairing_stuck : ∀ (p : natprod),
p = (fst p, snd p).
Proof.
simpl. (* Doesn't reduce anything! *)
Abort.
相反,我们需要公开p的结构,以便siml可以在fst和snd中执行模式匹配。我们可以用析构函数来实现这一点。
Theorem surjective_pairing : ∀ (p : natprod),
p = (fst p, snd p).
Proof.
intros p. destruct p as [n m]. simpl. reflexivity. Qed.
请注意,与NAT的行为不同,它在NAT中生成两个子目标,而destruct在这里只生成一个子目标。这是因为natprods只能以一种方式构造。
26: 1 star, standard (snd_fst_is_swap)
Theorem snd_fst_is_swap : ∀ (p : natprod),
(snd p, fst p) = swap_pair p.
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem snd_fst_is_swap : forall (p : natprod),
(snd p, fst p) = swap_pair p.
Proof.
intros p. destruct p as [n m]. simpl. reflexivity. Qed.
27: 1 star, standard, optional (fst_swap_is_snd)
Theorem fst_swap_is_snd : ∀ (p : natprod),
fst (swap_pair p) = snd p.
Proof.
(* FILL IN HERE *) Admitted.
Coq代码
Theorem fst_swap_is_snd : forall (p : natprod),
fst (swap_pair p) = snd p.
Proof.
intros p. destruct p as [n m]. simpl. reflexivity. Qed.