Coq基础(六) - Logic in Coq
Coq中的命题类型语句
Coq是一种类型化语言,这意味着它的世界中的每个合理表达式都有一个相关的类型。逻辑声明也不例外,任何一个可以证明的语句都有一个类型,即Prop,命题类型。
可以使用check命令查看这个类型:
Check 3 = 3.
(* ===> Prop *)
Check forall n m : nat, n + m = m + n.
(* ===> Prop *)
需要注意的是,任何符合命题语法定义的语句都是Prop类型的,不论它们是否成立,关键在于它们是否能够被证明:
Check 2 = 2.
(* ===> Prop *)
Check forall n : nat, n = 2.
(* ===> Prop *)
事实上,Prop类型的语句已经在前面的[Theorem]、[Lemma]以及[Example]语句中的声明中出现过了。
此外,还可以使用[Definition]语句对命题进行命名:
Definition plus_fact : Prop := 2 + 2 = 4.
Check plus_fact.
(* ===> plus_fact : Prop *)
然后可以在其他使用命题类型语句的地方用这个名称,比如[Theorem]语句中:
Theorem plus_fact_is_true :
plus_fact.
Proof. reflexivity. Qed.
还可以定义一些“带参数”的命题:
Definition injective {A B} (f : A -> B) :=
forall x y : A, f x = f y -> x = y.
Lemma succ_inj : injective S.
Proof.
intros n m H. injection H as H1. apply H1.
Qed.
合取
命题A和命题B的合取,也称为逻辑与,记为A/\B,只有当A和B同时成立的时候A/\B才成立:
Example and_example : 3 + 4 = 7 /\ 2 * 2 = 4.
如果证明目标是一个合取表达式,则需要使用[split]策略,它用于从合取表达式生成两个子目标:
Proof.
split.
- (* 3 + 4 = 7 *) reflexivity.
- (* 2 + 2 = 4 *) reflexivity.
Qed.
对于任何命题A和B,假设A是成立的,并且B也是成立的,则可以得出结论,A/\B也是成立的:
Lemma and_intro : forall A B : Prop, A -> B -> A /\ B.
Proof.
intros A B HA HB. split.
- apply HA.
- apply HB.
Qed.
如果需要用到的假设是一个合取表达式,那么可以使用[destruct]策略。如果证明上下文包含一个假设H,其形式为A/\B,那么destruct H as [HA HB]将把H从上下文中除去,并增加两个新的假设:HA,表示A为真;HB,表示B为真。
Lemma and_example2 :
forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
Proof.
(* WORKED IN CLASS *)
intros n m H.
destruct H as [Hn Hm].
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
也可以在使用[intro]关键字的时候直接将上下文中的假设写成两个新的假设:
Lemma and_example2 :
forall n m : nat, n = 0 /\ m = 0 -> n + m = 0.
Proof.
(* WORKED IN CLASS *)
intros n m [Hn Hm].
rewrite Hn. rewrite Hm.
reflexivity.
Qed.
关于合取,一个必然成立的结论是:如果A/\B成立,那么A必然成立,或者B必然成立:
Theorem and_commut : forall P Q : Prop,
P /\ Q -> Q /\ P.
Proof.
intros P Q [HP HQ].
split.
- (* left *) apply HQ.
- (* right *) apply HP. Qed.
析取
另外一个逻辑连词是析取,也叫做逻辑或,命题A和命题B的析取记为A/B,A/B成立,当且仅当A成立,或者B成立。
如果证明过程中的上下文中出现了析取表达式,那么就需要使用[destruct]进行分情况讨论:
Lemma or_example :
forall n m : nat, n = 0 \/ m = 0 -> n * m = 0.
Proof.
(* This pattern implicitly does case analysis on
n = 0 ∨ m = 0 *)
intros n m [Hn | Hm].
- (* Here, n = 0 *)
rewrite Hn. reflexivity.
- (* Here, m = 0 *)
rewrite Hm. rewrite <- mult_n_O.
reflexivity.
Qed.
相反的,如果需要证明一个析取表达式成立,那么需要证明这个表达式的哪边是成立的。可以使用两个策略[left]和[right],显而易见,它们的作用就是决定选择析取表达式的左边或者右边:
Lemma or_intro : forall A B : Prop, A -> A \/ B.
Proof.
intros A B HA.
left.
apply HA.
Qed.
假命题和否定
在前面的策略中有讲述过Coq中的[discriminate]策略时,提到过如果两个假设两个不同构造器构造的变量相等,那么就可以根据这个假设推导出任何结论,因为这个假设前提就是不成立的。
根据这个前提,可以将逻辑非~P定义成P->False,其中False是一个在Coq标准库中定义的一个矛盾命题
Module MyNot.
Definition not (P:Prop) := P -> False.
Notation "¬x" := (not x) : type_scope.
Check not.
(* ===> Prop -> Prop *)
End MyNot.
因为False是一个矛盾命题,那么根据前面所提到的那个原则,如果在证明过程中得到了一个矛盾命题False,那么可以直接使用[destruct]来完成整个证明目标:
Theorem ex_falso_quodlibet : forall (P:Prop),
False -> P.
Proof.
intros P contra.
destruct contra. Qed.
同样的,可以根据逻辑非来定义不相等:
Notation "x <> y" := (~(x = y)).
关于不相等命题的证明也和上面的一样:
Theorem zero_not_one : 0 <> 1.
Proof.
(** The proposition [0 <> 1] is exactly the same as
[~(0 = 1)], that is [not (0 = 1)], which unfolds to
[(0 = 1) -> False]. (We use [unfold not] explicitly here
to illustrate that point, but generally it can be omitted.) *)
unfold not.
(** To prove an inequality, we may assume the opposite
equality... *)
intros contra.
(** ... and deduce a contradiction from it. Here, the
equality [O = S O] contradicts the disjointness of
constructors [O] and [S], so [discriminate] takes care
of it. *)
discriminate contra.
Qed.
恒真命题
除了前面提到的矛盾命题,在coq标准库中也定义了恒真命题True:
Lemma True_is_true : True.
Proof.
(*To prove it, we use the predefined constant I : True:*)
apply I.
Qed.
逻辑等价
常见的“当且仅当”逻辑连词,意味着两个命题有着相同的真值,可以将其定义为两个蕴含的合取:
Module MyIff.
Definition iff (P Q : Prop) := (P -> Q) /\ (Q -> P).
Notation "P <-> Q" := (iff P Q)
(at level 95, no associativity)
: type_scope.
End MyIff.
逻辑等价命题的证明和前面的合取命题的证明是一样的:
Theorem iff_sym : forall P Q : Prop,
(P <-> Q) -> (Q <-> P).
Proof.
intros P Q [HAB HBA].
split.
- (* -> *) apply HBA.
- (* <- *) apply HAB. Qed.
存在量词
另外一个比较重要的逻辑连词是存在量词。如果说一个 T 类型的变量 x 满足属性 P,则记为exists x:T, P。
为了证明形如exists x:T,P.类型的命题,必须要证明对于某个特定的x,P属性成立。这称为存在的见证:
Lemma four_is_even : exists n : nat, 4 = n + n.
Proof.
exists 2. reflexivity.
Qed.
相反的,如果假设中有存在量词exists x:T,P,那么可以使用[destruct]来获取一个存在见证x以及假设x,P:
Theorem exists_example_2 : forall n,
(exists m, n = 4 + m) ->
(exists o, n = 2 + o).
Proof.
intros n [m Hm]. (* note implicit [destruct] here *)
exists (2 + m).
apply Hm. Qed.
命题编程
前面所见过的逻辑连接词为从简单命题定义复杂命题提供了丰富的词汇。例如,定义一个判断元素x是否在链表l中的函数,采取下面的结构:
- 如果l是空集,那么命题"x in l"显然不成立
- 否则,如果l是形如x'::l'的结构,那么命题"x in l"就蕴含"x = x' or x in l'"
所以这个函数可以定义为:
Fixpoint In {A : Type} (x : A) (l : list A) : Prop :=
match l with
| [] => False
| x' :: l' => x' = x \/ In x l'
end.
Example In_example_1 : In 4 [1; 2; 3; 4; 5].
Proof.
(* WORKED IN CLASS *)
simpl. right. right. right. left. reflexivity.
Qed.