IndPrinciples归纳法则
基础
Check nat_ind.
(* ===> nat_ind :
forall P : nat -> Prop,
P 0 ->
(forall n : nat, P n -> P (S n)) ->
forall n : nat, P n *)
In English: Suppose P is a property of natural numbers (that is,
P n is a Prop for every n). To show that P n holds of all
n, it suffices to show:
induction 利用归纳法则,执行 apply t_ind 等策略。
为了清楚地理解这一点,让我们直接使用 apply nat_ind 而非 induction
策略来证明。例如,Induction 一章中 mult_0_r' 定理的另一种证明如下所示。
- P holds of 0
- for any n, if P holds of n, then P holds of S n.
Theorem mult_0_r' : ∀ n:nat,
n × 0 = 0.
Proof.
apply nat_ind.
- (* n = O *) reflexivity.
- (* n = S n' *) simpl. intros n' IHn'. rewrite → IHn'.
reflexivity. Qed.
这个证明基本上等同于之前的,但有几点区别值得注意。
首先,在证明的归纳步骤("S" 情形)中,我们不得不手动管理变量名(即 intros),
而 induction 会自动完成这些。
其次,在应用 nat_ind 之前我们没有在上下文中引入 n —— nat_ind 的结论
是一个带有量词的公式,apply 需要这个结论精确地匹配当前证明目标状态的形状,包括其中的量词。
相反,induction 策略对于上下文中的变量或目标中由量词引入的变量都适用。
第三,我们必须手动为 apply 提供归纳法则,而 induction 可以自己解决它。
相比于直接使用 nat_ind 这样的归纳法则,在实践中使用 induction 更加方便。
但重要的是认识到除了这一点变量名的管理工作,我们在做的其实就是应用 nat_ind。
练习:2 星, standard, optional (plus_one_r')
请不使用 induction 策略来完成这个证明。t_ind : ∀ P : t → Prop,
... case for c1 ... →
... case for c2 ... → ...
... case for cn ... →
∀ n : t, P n
Inductive time : Type :=
| day
| night.
Check time_ind.
(* ===> time_ind : forall P : time -> Prop,
P day ->
P night ->
forall t : time, P t *)
Inductive natlist : Type :=
| nnil
| ncons (n : nat) (l : natlist).
Check natlist_ind.
(* ===>
natlist_ind :
forall P : natlist -> Prop,
P nnil ->
(forall (n : nat) (l : natlist),
P l -> P (ncons n l)) ->
forall l : natlist, P l *)
现在归纳法则会是什么呢? ☐
通常,为归纳类型 t 生成的归纳法则形式如下:
- 每个构造子 c 都会生成归纳法则中的一种情况
- 若 c 不接受参数,该情况为
“P 对 c 成立” - 若 c 接受参数 x1:a1 ... xn:an,该情况为:
“对于所有的 x1:a1 ... xn:an,
若 [P] 对每个类型为 [t] 的函数都成立,
则 [P] 对于 [c x1 ... xn] 成立”
练习:1 星, standard, optional (booltree_ind)
对如下数据类型,请写出 Coq 将会生成的归纳法则。 (与之前一样,先在纸上或注释内写下你的答案,然后同 Coq 打印出的结果比较。)Inductive booltree : Type :=
| bt_empty
| bt_leaf (b : bool)
| bt_branch (b : bool) (t1 t2 : booltree).
☐
练习:1 星, standard, optional (ex_set)
这是对一个归纳定义的集合的归纳法则。ExSet_ind :
∀ P : ExSet → Prop,
(∀ b : bool, P (con1 b)) →
(∀ (n : nat) (e : ExSet), P e → P (con2 n e)) →
∀ e : ExSet, P e
多态
Inductive list (X:Type) : Type :=
| nil : list X
| cons : X → list X → list X.
list_ind :
∀ (X : Type) (P : list X → Prop),
P [] →
(∀ (x : X) (l : list X), P l → P (x :: l)) →
∀ l : list X, P l
练习:1 星, standard, optional (tree)
对如下数据类型,请写出 Coq 将会生成的归纳法则,并与 Coq 打印出的结果比较。练习:1 星, standard, optional (mytype)
请找到对应于以下归纳法则的归纳定义:mytype_ind :
∀ (X : Type) (P : mytype X → Prop),
(∀ x : X, P (constr1 X x)) →
(∀ n : nat, P (constr2 X n)) →
(∀ m : mytype X, P m →
∀ n : nat, P (constr3 X m n)) →
∀ m : mytype X, P m
练习:1 星, standard, optional (foo)
请找到对应于以下归纳法则的归纳定义:foo_ind :
∀ (X Y : Type) (P : foo X Y → Prop),
(∀ x : X, P (bar X Y x)) →
(∀ y : Y, P (baz X Y y)) →
(∀ f1 : nat → foo X Y,
(∀ n : nat, P (f1 n)) → P (quux X Y f1)) →
∀ f2 : foo X Y, P f2
练习:1 星, standard, optional (foo')
请考虑以下归纳定义:
Coq 会为 foo' 生成什么归纳法则?请填写下面的空白,并使用 Coq 检查你的答案。
foo'_ind :
∀ (X : Type) (P : foo' X → Prop),
(∀ (l : list X) (f : foo' X),
_______________________ →
_______________________ ) →
___________________________________________ →
∀ f : foo' X, ________________________
☐
foo'_ind :
∀ (X : Type) (P : foo' X → Prop),
(∀ (l : list X) (f : foo' X),
_______________________ →
_______________________ ) →
___________________________________________ →
∀ f : foo' X, ________________________
归纳假设
∀ P : nat → Prop,
P 0 →
(∀ n : nat, P n → P (S n)) →
∀ n : nat, P n
……或等价地:
现在看看 P_m0r 在证明中出现在哪里。
Theorem mult_0_r'' : ∀ n:nat,
P_m0r n.
Proof.
apply nat_ind.
- (* n = O *) reflexivity.
- (* n = S n' *)
(* 请注意目前的证明状态! *)
intros n IHn.
unfold P_m0r in IHn. unfold P_m0r. simpl. apply IHn. Qed.
通常我们并不会在证明中额外地为命题命名,但有意地进行一两个练习
可以帮助我们清晰地看到哪个是归纳假设。
如果对 n 归纳来证明 ∀ n, P_m0r n(使用 induction
或 apply nat_ind),可以看到第一个子目标要求我们证明 P_m0r 0
(“P 对零成立”),而第二个子目标要求我们证明 ∀ n', P_m0r n' → P_m0r (S n')
(也即,“P 对 S n' 成立仅当其对 n' 成立”,或者说,“P 被 S 保持”)。
'归纳假设'是后一个蕴含式中的前提部分,即假设 P 对 n' 成立,这是我们在证明 P
对 S n' 的过程中允许使用的。
深入 induction 策略
- 如果 P n 是某个涉及到数字 n 的命题,我们想要证明 P 对于'所有'数字 n
都成立,我们以如下方式推理:
- 证明 P 0 成立
- 证明如果 P n' 成立,那么 P (S n') 成立
- 得出结论 P n 对所有 n 成立。
Theorem plus_assoc' : ∀ n m p : nat,
n + (m + p) = (n + m) + p.
Proof.
(* ……首先,我们引入全部 3 个变量到上下文中,或者说是
“考虑任意的 n,m 和 p……” *)
intros n m p.
(* ……现在,我们用 induction 策略来证明 P n
(也即,n + (m + p) = (n + m) + p)对全体的 n 成立,
也因此对于当前上下文中特殊的 n 也成立。 *)
induction n as [| n'].
- (* n = O *) reflexivity.
- (* n = S n' *)
simpl. rewrite → IHn'. reflexivity. Qed.
对目标中含有量词的变量应用 induction 同样可以工作。
Theorem plus_comm' : ∀ n m : nat,
n + m = m + n.
Proof.
induction n as [| n'].
- (* n = O *) intros m. rewrite <- plus_n_O. reflexivity.
- (* n = S n' *) intros m. simpl. rewrite → IHn'.
rewrite <- plus_n_Sm. reflexivity. Qed.
请注意,使用 induction 后 m 在目标中仍然是绑定的,
也即,归纳证明的陈述是以 ∀ m 开始的。
如果我们对目标中其他量词'后'的量化变量使用 induction,那么它会自动
引入全部被量词绑定的变量到上下文中。
Theorem plus_comm'' : ∀ n m : nat,
n + m = m + n.
Proof.
(* 这次让我们对 m 而非 n 进行归纳…… *)
induction m as [| m']. (* n 已经引入到上下文中了 *)
- (* m = O *) simpl. rewrite <- plus_n_O. reflexivity.
- (* m = S m' *) simpl. rewrite <- IHm'.
rewrite <- plus_n_Sm. reflexivity. Qed.
练习:1 星, standard, optional (plus_explicit_prop)
以类似 mult_0_r'' 的方式来重写 plus_assoc',plus_comm' 和它们的证明—— 对于每个定理,给出一个明确的命题的 Definition,陈述定理并用归纳法证明这个 定义的命题。(* 请在此处解答 *)
☐
Prop 中的归纳法则
Inductive even : nat → Prop :=
| ev_0 : even 0
| ev_SS : ∀ n : nat, even n → even (S (S n)).
ev_ind_max : ∀ P : (∀ n : nat, even n → Prop),
P O ev_0 →
(∀ (m : nat) (E : even m),
P m E →
P (S (S m)) (ev_SS m E)) →
∀ (n : nat) (E : even n),
P n E
- 由于 even 被自然数 n 所索引(任何 even 对象 E 都是某个自然数 n
是偶数的证据),且命题 P 同时被 n 和 E 所参数化——因此,被用于证明断言的
归纳法则同时涉及到一个偶数和这个数是偶数的证据。
- 由于有两种方法来给出偶数性质的证据(因为 even 有两个构造子),我们应用归纳法则生成
了两个子目标:
- 须证明 P 对 0 和 ev_0 成立。
- 须证明,当 m 是一个自然数且 E 是其偶数性质的证据,如果 P
对 m 和 E 成立,那么它也对 S (S m) 和 ev_SS m E 成立。
- 须证明 P 对 0 和 ev_0 成立。
- 如果这些子目标可以被证明,那么归纳法则告诉我们 P 对所有的偶数 n 和它们的偶数性质 E 成立。
∀ P : nat → Prop,
... →
∀ n : nat,
even n → P n
Print ev.
(* ===>
Inductive ev : nat -> Prop :=
| ev_0 : ev 0
| ev_SS : forall n : nat, ev n -> ev (S (S n))
*)
Check ev_ind.
(* ===> ev_ind
: forall P : nat -> Prop,
P 0 ->
(forall n : nat, ev n -> P n -> P (S (S n))) ->
forall n : nat,
ev n -> P n *)
请特别注意,Coq 丢弃了命题 P 参数中的证据项 E。
若用自然语言来表述 ev_ind,则是说:假设 P 是关于自然数的一个性质
(也即,P n 是一个在全体 n 上的 Prop)。为了证明当 n 是偶数时
P n 成立,需要证明:
正如期待的那样,我们可以不使用 induction 而直接应用 ev_ind。
比如,我们可以使用它来证明 ev'(就是在 IndProp
一章的练习中那个有点笨拙的偶数性质的定义)等价于更简洁的归纳定义 ev:
- P 对 0 成立,
- 对任意 n,如果 n 是偶数且 P 对 n 成立,那么 P 对 S (S n) 成立。
Inductive ev' : nat → Prop :=
| ev'_0 : ev' 0
| ev'_2 : ev' 2
| ev'_sum n m (Hn : ev' n) (Hm : ev' m) : ev' (n + m).
Theorem ev_ev' : ∀ n, ev n → ev' n.
Proof.
apply ev_ind.
- (* ev_0 *)
apply ev'_0.
- (* ev_SS *)
intros m Hm IH.
apply (ev'_sum 2 m).
+ apply ev'_2.
+ apply IH.
Qed.
Inductive 定义的具体形式会影响到 Coq 生成的归纳法则。
Inductive le1 : nat → nat → Prop :=
| le1_n : ∀ n, le1 n n
| le1_S : ∀ n m, (le1 n m) → (le1 n (S m)).
Notation "m <=1 n" := (le1 m n) (at level 70).
此定义其实可以稍微简化一点,我们观察到左侧的参数 n
在定义中始终是相同的,于是可以把它变成整体定义中的一个“一般参数”,
而非每个构造子的参数。
Inductive le2 (n:nat) : nat → Prop :=
| le2_n : le2 n n
| le2_S m (H : le2 n m) : le2 n (S m).
Notation "m <=2 n" := (le2 m n) (at level 70).
尽管第二个看起来不那么对称了,但它却更好一点。
为什么呢?因为它会生成更简单的归纳法则。
Check le1_ind.
(* ===> forall P : nat -> nat -> Prop,
(forall n : nat, P n n) ->
(forall n m : nat, n <=1 m -> P n m -> P n (S m)) ->
forall n n0 : nat, n <=1 n0 -> P n n0 *)
Check le2_ind.
(* ===> forall (n : nat) (P : nat -> Prop),
P n ->
(forall m : nat, n <=2 m -> P m -> P (S m)) ->
forall n0 : nat, n <=2 n0 -> P n0 *)
形式化 vs. 非形式化的归纳证明
对归纳定义的集合进行归纳
- '定理': <有形如“For all n:S, P(n)”的全称量化命题,其中 S
是某个归纳定义的集合。>
- 假设 n = c a1 ... ak,其中 <…… 这里我们为每个具有类型 S 的 a 陈述其归纳假设(IH)> 。
我们需要证明 <…… 我们在这里重新陈述 P(c a1 ... ak)>。
- <其他情形以此类推……> ☐
- 假设 n = c a1 ... ak,其中 <…… 这里我们为每个具有类型 S 的 a 陈述其归纳假设(IH)> 。
我们需要证明 <…… 我们在这里重新陈述 P(c a1 ... ak)>。
- '定理': 对所有集合 X, 列表 l : list X,以及数字 n,如果
length l = n 那么 index (S n) l = None。
- 假设 l = []。我们需要证明,对于任意数字 n,如果 length [] = n,那么
index (S n) [] = None。
- 假设 l = x :: l' 对某个 x 和 l',其中 length l' = n' 对任意数字 n'
蕴含了 index (S n') l' = None。我们需要证明,对任意数字 n,如果
length (x::l') = n 那么 index (S n) (x::l') = None。
length l = length (x::l') = S (length l'),
index (S (length l')) l' = None.
- 假设 l = []。我们需要证明,对于任意数字 n,如果 length [] = n,那么
index (S n) [] = None。
对归纳定义的命题进行归纳
- '定理': <有形如“Q → P”的命题,其中 Q 是某个归纳定义的命题
(更一般地,“对任意 x y z,Q x y z → P x y z”)>
- 假设被用于证明 Q 的最终规则是 c。那么<……我们在这里陈述所有 a 的类型,
从构造子的定义中得到的任何等式,以及每个具有类型 Q 的 a 的归纳假设>。
我们需要证明<……我们在这里重新陈述 P>。
- <其他情形以此类推……> ☐
- 假设被用于证明 Q 的最终规则是 c。那么<……我们在这里陈述所有 a 的类型,
从构造子的定义中得到的任何等式,以及每个具有类型 Q 的 a 的归纳假设>。
我们需要证明<……我们在这里重新陈述 P>。
- '定理': ≤ 关系是传递的,也即,对任意数字 n,m 和 o,如果
n ≤ m 且 m ≤ o 那么 n ≤ o。
- 假设被用于证明 m ≤ o 的最终规则是 le_n。
那么 m = o 且我们需要证明 n ≤ m,其可从假设中直接得出。
- 假设被用于证明 m ≤ o 的最终规则是 le_S。
那么 o = S o' 对某个 o' 且 m ≤ o'。我们需要证明 n ≤ S o'。
由归纳假设得出,n ≤ o'。
- 假设被用于证明 m ≤ o 的最终规则是 le_n。
那么 m = o 且我们需要证明 n ≤ m,其可从假设中直接得出。
Explicit Proof Objects for Induction (Optional)
Check nat_ind.
(* ===>
nat_ind : forall P : nat -> Prop,
P 0 ->
(forall n : nat, P n -> P (S n)) ->
forall n : nat, P n *)
There's nothing magic about this induction lemma: it's just
another Coq lemma that requires a proof. Coq generates the proof
automatically too...
Print nat_ind.
(* ===> (after some slight tidying)
nat_ind :=
fun (P : nat -> Prop)
(f : P 0)
(f0 : forall n : nat, P n -> P (S n)) =>
fix F (n : nat) : P n :=
match n with
| 0 => f
| S n0 => f0 n0 (F n0)
end.
*)
We can read this as follows:
Suppose we have evidence f that P holds on 0, and
evidence f0 that ∀ n:nat, P n → P (S n).
Then we can prove that P holds of an arbitrary nat n via
a recursive function F (here defined using the expression
form Fix rather than by a top-level Fixpoint
declaration). F pattern matches on n:
We can adapt this approach to proving nat_ind to help prove
non-standard induction principles too. As a motivating example,
suppose that we want to prove the following lemma, directly
relating the ev predicate we defined in IndProp
to the evenb function defined in Basics.
- If it finds 0, F uses f to show that P n holds.
- If it finds S n0, F applies itself recursively on n0 to obtain evidence that P n0 holds; then it applies f0 on that evidence to show that P (S n) holds.
Lemma evenb_ev : ∀ n: nat, evenb n = true → ev n.
Proof.
induction n; intros.
- apply ev_0.
- destruct n.
+ simpl in H. inversion H.
+ simpl in H.
apply ev_SS.
Abort.
Attempts to prove this by standard induction on n fail in the case for
S (S n), because the induction hypothesis only tells us something about
S n, which is useless. There are various ways to hack around this problem;
for example, we can use ordinary induction on n to prove this (try it!):
Lemma evenb_ev' : ∀ n : nat,
(evenb n = true → ev n) ∧ (evenb (S n) = true → ev (S n)).
But we can make a much better proof by defining and proving a
non-standard induction principle that goes "by twos":
Definition nat_ind2 :
∀ (P : nat → Prop),
P 0 →
P 1 →
(∀ n : nat, P n → P (S(S n))) →
∀ n : nat , P n :=
fun P ⇒ fun P0 ⇒ fun P1 ⇒ fun PSS ⇒
fix f (n:nat) := match n with
0 ⇒ P0
| 1 ⇒ P1
| S (S n') ⇒ PSS n' (f n')
end.
Once you get the hang of it, it is entirely straightforward to
give an explicit proof term for induction principles like this.
Proving this as a lemma using tactics is much less intuitive.
The induction ... using tactic variant gives a convenient way to
utilize a non-standard induction principle like this.
Lemma evenb_ev : ∀ n, evenb n = true → ev n.
Proof.
intros.
induction n as [ | |n'] using nat_ind2.
- apply ev_0.
- simpl in H.
inversion H.
- simpl in H.
apply ev_SS.
apply IHn'.
apply H.
Qed.
The Coq Trusted Computing Base
- Since Coq types can themselves be expressions, the checker must
normalize these (by using the computation rules) before
comparing them.
- The checker must make sure that match expressions are
exhaustive. That is, there must be an arm for every possible
constructor. To see why, consider the following alleged proof
object:
Definition or_bogus : ∀ P Q, P ∨ Q → P :=
fun (P Q : Prop) (A : P ∨ Q) ⇒
match A with
| or_introl H ⇒ H
end. - The checker must make sure that each fix expression
terminates. It does this using a syntactic check to make sure
that each recursive call is on a subexpression of the original
argument. To see why this is essential, consider this alleged
proof:
Definition nat_false : ∀ (n:nat), False :=
fix f (n:nat) : False := f n.
(* 2022-03-14 05:26:58 (UTC+00) *)