Coq基础(八) - Imp
除了能用Coq完成前面所介绍过的各种数学证明,还可以用Coq研究一些其他的事情。Coq中包含一种简单的命令式编程语言Imp,它包含了传统主流语言(如C和Java)的一小部分核心。
下面是一个用Imp语言编写的一个熟悉的数学函数:
Z ::= X;;
Y ::= 1;;
WHILE ~(Z = 0) DO
Y ::= Y * Z;;
Z ::= Z - 1
END
而这里只是对Imp编程语言的语法和语义进行简单的介绍
算术和布尔表达式
语法:定义算术表达式和布尔表达式的两个抽象语法:
Inductive aexp : Type :=
| ANum (n : nat)
| APlus (a1 a2 : aexp)
| AMinus (a1 a2 : aexp)
| AMult (a1 a2 : aexp).
Inductive bexp : Type :=
| BTrue
| BFalse
| BEq (a1 a2 : aexp)
| BLe (a1 a2 : aexp)
| BNot (b : bexp)
| BAnd (b1 b2 : bexp).
基于上面的定义,可以将一个自然数表达式"1 + 2 * 3"翻译成下例代码:
APlus (ANum 1) (AMult (ANum 2) (ANum 3)).
赋值:对算术表达式求值会产生一个数字:
Fixpoint aeval (a : aexp) : nat :=
match a with
| ANum n ⇒ n
| APlus a1 a2 ⇒ (aeval a1) + (aeval a2)
| AMinus a1 a2 ⇒ (aeval a1) - (aeval a2)
| AMult a1 a2 ⇒ (aeval a1) * (aeval a2)
end.
Example test_aeval1:
aeval (APlus (ANum 2) (ANum 2)) = 4.
Proof. reflexivity. Qed.
类似地,计算一个布尔表达式会得到一个布尔值:
Fixpoint beval (b : bexp) : bool :=
match b with
| BTrue ⇒ true
| BFalse ⇒ false
| BEq a1 a2 ⇒ (aeval a1) =? (aeval a2)
| BLe a1 a2 ⇒ (aeval a1) <=? (aeval a2)
| BNot b1 ⇒ negb (beval b1)
| BAnd b1 b2 ⇒ andb (beval b1) (beval b2)
end.
优化:Coq中还没有定义的东西有很多,但是可以从已经定义的东西中得到一些里程。比如定义一个函数,它接受一个算术表达式并稍微简化它,将每个0 + e(即。(APlus (ANum 0) e)变成e。
Fixpoint optimize_0plus (a:aexp) : aexp :=
match a with
| ANum n ⇒ ANum n
| APlus (ANum 0) e2 ⇒ optimize_0plus e2
| APlus e1 e2 ⇒ APlus (optimize_0plus e1) (optimize_0plus e2)
| AMinus e1 e2 ⇒ AMinus (optimize_0plus e1) (optimize_0plus e2)
| AMult e1 e2 ⇒ AMult (optimize_0plus e1) (optimize_0plus e2)
end.
(*Verify*)
Example test_optimize_0plus:
optimize_0plus (APlus (ANum 2)
(APlus (ANum 0)
(APlus (ANum 0) (ANum 1))))
= APlus (ANum 2) (ANum 1).
Proof. reflexivity. Qed.
但是如果想要确保优化是正确的,也就是说,对一个优化表达式求值得到的结果与原来的结果相同:
Theorem optimize_0plus_sound: ∀a,
aeval (optimize_0plus a) = aeval a.
Proof.
intros a. induction a.
- (* ANum *) reflexivity.
- (* APlus *) destruct a1 eqn:Ea1.
+ (* a1 = ANum n *) destruct n eqn:En.
* (* n = 0 *) simpl. apply IHa2.
* (* n <> 0 *) simpl. rewrite IHa2. reflexivity.
+ (* a1 = APlus a1_1 a1_2 *)
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
+ (* a1 = AMinus a1_1 a1_2 *)
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
+ (* a1 = AMult a1_1 a1_2 *)
simpl. simpl in IHa1. rewrite IHa1.
rewrite IHa2. reflexivity.
- (* AMinus *)
simpl. rewrite IHa1. rewrite IHa2. reflexivity.
- (* AMult *)
simpl. rewrite IHa1. rewrite IHa2. reflexivity. Qed.
Coq自动化
上面的最后一个证明中相同步骤重复的次数比较多。如果算术表达式的语言或被证明合理的优化要复杂得多,这将成为一个真正的问题。
这个问题是因为忽略了Coq自动构造部分证明的强大功能。这里将介绍其中一些工具,习惯它们需要一些精力——Coq的自动化是一种强大的工具——但它将允许使用者将精力更多的用到更复杂的定义和更有趣的属性上,而不是无聊、重复、低层次的细节。
Tacticals:这里将其翻译为战术,这是Coq中一类将策略(Tactics)作为参数的策略,可以将其理解为高等策略
[Try]战术
假设T是一个策略,如果T无法不能成功执行,那么try T也并不会做任何事情,但是证明过程不会中止;如果T能够成功执行,那么try T和T是一样的:
Theorem silly1 : ∀ae, aeval ae = aeval ae.
Proof. try reflexivity. (* This just does reflexivity. *) Qed.
Theorem silly2 : ∀(P : Prop), P → P.
Proof.
intros P HP.
try reflexivity. (* Just reflexivity would have failed. *)
apply HP. (* We can still finish the proof in some other way. *)
Qed.
虽然看上去[try]战术并有什么用,但是它通常是和[;]战术一起使用的
[;]战术
[;]战术需要接受两个策略作为参数,语句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]战术和[;]一起使用,就可以对刚刚证明"Opimize_0plus_sound"进行简化:
Theorem optimize_0plus_sound': ∀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.
[repeat]战术
[repeat]战术将会重复一个策略:
Theorem In10 : In 10 [1;2;3;4;5;6;7;8;9;10].
Proof.
repeat (try (left; reflexivity); right).
Qed.
语句repeat T,并不是执行失败,如果策略T并不能够用于原来的目标,那么repeat T并不会中止证明过程,并且也不会改变原来的目标,如下:
Theorem In10' : In 10 [1;2;3;4;5;6;7;8;9;10].
Proof.
repeat (left; reflexivity).
repeat (right; try (left; reflexivity)).
Qed.