Coq Automation
最后一个证明中重复的次数有点烦人。如果算术表达式的语言或被证明合理的优化明显更复杂,那么它将开始成为一个真正的问题。
到目前为止,我们所做的所有证明都只使用了Coq的一小部分策略,而完全忽略了它自动构造部分证明的强大功能。本节将介绍其中的一些设施,我们将在接下来的几章中看到更多内容。习惯它们需要一些精力——Coq的自动化是一个强有力的工具——但它将允许我们扩大我们的工作范围,以获得更复杂的定义和更有趣的属性,而不会被无聊、重复、低级的细节所淹没。
Tacticals
战术是Coq对以其他战术为论据的战术的术语——“高阶战术”,如果你愿意的话。
The try Tactical
如果T是一种策略,那么try T是一种与T类似的策略,只是如果T失败,try T成功地什么都不做(而不是失败)。
Theorem silly1 : forall ae, aeval ae = aeval ae.
Proof. try reflexivity. //这就是反射性。
Qed.
Theorem silly2 : forall (P : Prop), P -> P.
Proof.
intros P HP.
try reflexivity. //仅仅是自反性就会失败。
apply HP.//我们仍然可以用其他方式完成证明。
Qed.
并没有真正的理由在像这样的完全手工校对中使用try,但它对于结合自动化校对是非常有用的;战术,我们接下来展示。
The ; Tactical (Simple Form)
最常见的形式是;战术以两种战术作为论据。复合战术T;T'首先执行T,然后对T生成的每个子目标执行T'。
例如,考虑下面的微不足道引理:
Lemma foo : forall n, 0 <=? n = true.
Proof.
intros.
destruct n.
(* Leaves two subgoals, which are discharged identically... *)
- (* n=0 *) simpl. reflexivity.
- (* n=Sn' *) simpl. reflexivity.
Qed.
我们可以使用;策略简化此证明:
Lemma foo' : forall n, 0 <=? n = true.
Proof.
intros.
(* [destruct] the current goal *)
destruct n;
(* then [simpl] each resulting subgoal *)
simpl;
(* and do [reflexivity] on each resulting subgoal *)
reflexivity.
Qed.
同时使用try和;,我们可以消除刚才困扰我们的证明中的重复。
Theorem optimize_0plus_sound': forall a,
aeval (optimize_0plus a) = aeval a.
Proof.
intros a.
induction a;
(* Most cases follow directly by the IH... *)
try (simpl; rewrite IHa1; rewrite IHa2; reflexivity).
(* ... but the remaining cases -- ANum and APlus --
are different: *)
- (* ANum *) reflexivity.
- (* APlus *)
destruct a1 eqn:Ea1;
(* Again, most cases follow directly by the IH: *)
try (simpl; simpl in IHa1; rewrite IHa1;
rewrite IHa2; reflexivity).
(* The interesting case, on which the [try...]
does nothing, is when [e1 = ANum n]. In this
case, we have to destruct [n] (to see whether
the optimization applies) and rewrite with the
induction hypothesis. *)
+ (* a1 = ANum n *) destruct n eqn:En;
simpl; rewrite IHa2; reflexivity. Qed.
Coq专家经常使用这个“……;try……”成语经过一个像归纳法这样的策略,一次处理了许多类似的情况。当然,这种做法在非正式的证明中也有相似之处。例如,下面是优化定理的非正式证明,它与正式定理的结构相匹配:
_Theorem_: For all arithmetic expressions [a],
aeval (optimize_0plus a) = aeval a.
_Proof_: By induction on [a]. Most cases follow directly from the
IH. The remaining cases are as follows:
- Suppose [a = ANum n] for some [n]. We must show
aeval (optimize_0plus (ANum n)) = aeval (ANum n).
This is immediate from the definition of [optimize_0plus].
- Suppose [a = APlus a1 a2] for some [a1] and [a2]. We must
show
aeval (optimize_0plus (APlus a1 a2)) = aeval (APlus a1 a2).
Consider the possible forms of [a1]. For most of them,
[optimize_0plus] simply calls itself recursively for the
subexpressions and rebuilds a new expression of the same form
as [a1]; in these cases, the result follows directly from the
IH.
The interesting case is when [a1 = ANum n] for some [n]. If
[n = 0], then
optimize_0plus (APlus a1 a2) = optimize_0plus a2
and the IH for [a2] is exactly what we need. On the other
hand, if [n = S n'] for some [n'], then again [optimize_0plus]
simply calls itself recursively, and the result follows from
the IH.
然而,这一证明仍然可以改进:第一个案例(对于a=ANum n)非常琐碎——甚至比我们所说的简单地遵循IH的案例更琐碎——但我们选择将其全部写出来。最好是把它放下,在最上面说,“大多数案件要么是直接的,要么是来自IH的。唯一有趣的案件是APlus的案件……”我们也可以在形式证明上做同样的改进。下面是它的外观:
Theorem optimize_0plus_sound'': forall a,
aeval (optimize_0plus a) = aeval a.
Proof.
intros a.
induction a;
(* Most cases follow directly by the IH *)
try (simpl; rewrite IHa1; rewrite IHa2; reflexivity);
(* ... or are immediate by definition *)
try reflexivity.
(* The interesting case is when a = APlus a1 a2. *)
- (* APlus *)
destruct a1; try (simpl; simpl in IHa1; rewrite IHa1;
rewrite IHa2; reflexivity).
+ (* a1 = ANum n *) destruct n;
simpl; rewrite IHa2; reflexivity. Qed.
The ; Tactical (General Form)
这个战术也有比简单T更一般的形式;我们已经在上面看到了。如果T,T1。。。,Tn是战术,然后是T;[T1 | T2 |…| Tn]是一种策略,首先执行T,然后对T生成的第一个子目标执行T1,对第二个子目标执行T2,等等。
所以T;T’只是所有Ti都是相同策略时的特殊符号;i、 e,T;T’是:T的简写;[T'| T'|…| T']
The repeat Tactical
重复战术采用另一种战术,并持续应用该战术,直到无法取得进展。下面是一个示例,使用repeat显示10在一个长列表中。
Theorem In10
Theorem In10 : In 10 [1;2;3;4;5;6;7;8;9;10].
Proof.
repeat (try (left; reflexivity); right).
Qed.
战术重复T从不失败:如果战术T不适用于原始目标,那么重复仍然成功,而不改变原始目标(即重复零次)。
Theorem In10' : In 10 [1;2;3;4;5;6;7;8;9;10].
Proof.
repeat simpl.
repeat (left; reflexivity).
repeat (right; try (left; reflexivity)).
Qed.
战术重复T也没有应用T次数的上限。如果T是一个总是成功(并取得进展)的战术,那么重复T将永远循环。
Theorem repeat_loop
Theorem repeat_loop : forall (m n : nat),
m + n = n + m.
Proof.
intros m n.
(* Uncomment the next line to see the infinite loop occur.
In Proof General, [C-c C-c] will break out of the loop. *)
(* repeat rewrite Nat.add_comm. *)
Admitted.
虽然Coq术语语言Gallina的评估保证终止,但战术评估不终止!然而,这并不影响Coq的逻辑一致性,因为重复和其他策略的工作是指导Coq构造证明;如果构建过程出现分歧(即,它没有终止),这仅仅意味着我们没有构建证据,而不是我们构建了错误的证据。
Exercise: 3 stars, standard (optimize_0plus_b_sound)
由于optimize_0plus转换不会更改aexps的值,因此我们应该能够将其应用于bexp中出现的所有aexps,而无需更改bexp的值。编写一个在bexps上执行此转换的函数,并证明它是正确的。使用我们刚刚看到的战术,使证据尽可能优雅。
Fixpoint optimize_0plus_b (b : bexp) : bexp
(* REPLACE THIS LINE WITH ":= _your_definition_ ." *). Admitted.
Theorem optimize_0plus_b_sound : forall b,
beval (optimize_0plus_b b) = beval b.
Proof.
(* FILL IN HERE *) Admitted.
Fixpoint optimize_0plus_b (b : bexp) : bexp
Fixpoint optimize_0plus_b (b : bexp) : bexp :=
match b with
| BEq l r => BEq (optimize_0plus l) (optimize_0plus r)
| BLe l r => BLe (optimize_0plus l) (optimize_0plus r)
| BNot b' => BNot (optimize_0plus_b b')
| BAnd l r => BAnd (optimize_0plus_b l) (optimize_0plus_b r)
| _ => b
end.
Theorem optimize_0plus_b_sound
Theorem optimize_0plus_b_sound : forall b,
beval (optimize_0plus_b b) = beval b.
Proof.
intros.
induction b;
try (simpl; reflexivity);
try (simpl; repeat rewrite optimize_0plus_sound; reflexivity).
- simpl. rewrite IHb. reflexivity.
- simpl. rewrite IHb1. rewrite IHb2. reflexivity.
Qed.