更多内容见 https://github.com/january147/software_foundations_notes
Coq
Coq’s native functional programming language, called Gallina.
注释
(* 注释内容 * )
分隔符
Coq使用.
(英文句号)来分隔表达式。
显示类型
使用Check
关键字来检查一个表达式的类型
Check true.
定义类型
enumerated types
Inductive day : Type :=
| monday
| tuesday
| wednesday
| thursday
| friday
| saturday
| sunday.
使用已有类型定义新类型
Inductive rgb : Type :=
| red
| green
| blue.
Inductive color : Type :=
| black
| white
| primary (p:rgb)
red, green, blue, black
等等被称为constructor,给constructor设置所需的参数(可以没有参数)就得到一个constructor expression, 参数也是constructor expression。
red (* constructor *)
red (* 无参数的constructor expression *)
primary red (* 有参数的constructor expression *)
An Inductive definition carves out(分离) a subset of the whole space of constructor expressions and gives it a name, like bool, rgb, or color.
Definition monochrome (c : color) : bool :=
match c with
| black ⇒ true
| white ⇒ true
| primary p ⇒ false
end.
Definition isred (c : color) : bool :=
match c with
| black ⇒ false
| white ⇒ false
| primary red ⇒ true
| primary _ ⇒ false
end.
Since the primary constructor takes an argument, a pattern matching primary should include either a variable (as above – note that we can choose its name freely) or a constant of appropriate type.
The pattern “primary _” here is shorthand for “the constructor primary applied to any rgb constructor except red.” (The wildcard pattern _ has the same effect as the dummy pattern variable p in the definition of monochrome.)
定义函数
Definition next_weekday (d:day) : day :=
match d with
| monday ⇒ tuesday
| tuesday ⇒ wednesday
| wednesday ⇒ thursday
| thursday ⇒ friday
| friday ⇒ monday
| saturday ⇒ monday
| sunday ⇒ monday
end.
计算函数结果
使用Compute
关键字可以计算并输出一个函数执行结果
Compute (next_weekay tuesday).
定义模块
Module TestModule.
Definition b : rgb := blue.
End TestModule.
元组(Tuples)
使用接收多个参数的constructor
可以定义一个元组
Inductive bit : Type :=
| B0
| B1.
Inductive nybble : Type :=
| bits (b0 b1 b2 b3 : bit).
Check (bits B1 B0 B1 B0)
: nybble.
Definition all_zero (nb : nybble) : bool :=
match nb with
| (bits B0 B0 B0 B0) ⇒ true
| (bits _ _ _ _) ⇒ false
end.
We use underscore (_) as a wildcard pattern
使用递归定义的函数
Fixpoint evenb (n:nat) : bool :=
match n with
| O ⇒ true
| S O ⇒ false
| S (S n') ⇒ evenb n'
end.
For most interesting computations involving numbers, simple pattern matching is not enough: we also need recursion. For example, to check that a number n is even, we may need to recursively check whether n-2 is even. Such functions are introduced with the keyword
Fixpoint
instead ofDefinition
.
使用 Notion
进行简化
使用Notion
可以定义函数的简化表示
Notation "x + y" := (plus x y)
(at level 50, left associativity)
: nat_scope.
证明
Proof by Simplification
使用Example
来定义一个证明目标,使用Proof
开始一段证明,使用Qed
表示证明结束。
(* 定义子目标 *)
Example test_next_weekday:
(next_weekday (next_weekday saturday)) = tuesday.
Proof. simpl. reflexivity. Qed.
simpl
, reflexivity
被称为tactic,tactic是用于描述证明流程的命令,通过组合使用tactic来实现对目标的证明。simpl
还可以使用Theorem
来定义一个证明目标
Theorem plus_O_n : forall n : nat, 0 + n = n.
Proof.
intros m. reflexivity. Qed.
First, we’ve used the keyword
Theorem
instead ofExample
. This difference is mostly a matter of style; the keywords Example and Theorem (and a few others, including Lemma, Fact, and Remark) mean pretty much the same thing to Coq.
Proof by Rewrite
通常使用simpl
化简时,coq会根据各个运算的定义进行展开,而使用rewrite
可以让coq根据已有的假设或者定理对目标进行中匹配的部分进行改写。
Theorem mult_n_1 : forall p : nat,
p * 1 = p.
Proof.
intros p.
(** 此处目标为 p * (S O) = p, 注意1 = S O
定理 mult_n_Sm为 n * m + n = n * (S m), 目标中有同该定理匹配的
部分(即p * (S O) 匹配 n * (S m)),因此可以用rewrite改写为左边得
到 p * O + p = p *)
rewrite <- mult_n_Sm.
(** 此处目标为 p * O + p = p,
定理 mult_n_O为 O = n * O, 目标中有同该定理匹配的
部分(即p * O 匹配 n * O),因此可以用rewrite改写为左边得
到 O + p = p *)
rewrite <- mult_n_O.
(** 根据 + 的定义化简即可)
simpl.
reflexivity.
Qed.
Proof by Case analysis
使用destruct
对变量进行分类讨论。destruct
会生成子目标以及对应每个目标的新假设, 使用eqn:E
,可以将子目标中的新假设命名为E
。
Theorem andb_true_elim2 : forall b c : bool,
andb b c = true -> c = true.
Proof.
intros b c. destruct c eqn:Ec.
(* 子目标1,将假设c = true命名为Ec*)
-intros H.
reflexivity.
(* 子目标2,将假设c = false命名为Ec*)
-destruct b eqn:Eb.
--intros G.
rewrite <- G.
reflexivity.
--intros G2.
rewrite <- G2.
reflexivity.
Qed.
destruct
按照变量的定义方式来分解变量的,例如对于bool
类型,
Inductive bool : Type :=
| true
| false.
destruct
会将变量分解为true
和false
两种情况;对于nat
类型
Inductive nat : Type :=
| O
| S (n : nat).
则会将变量分解为O
以及S n
两种情况。
tactics
intros
可以使用intros
引入一个变量或者假设,intros
会按照目标中定义的各个变量和假设的顺序进行引入
Theorem t1 : forall n m : nat, n = m -> n + n = m + m.
Proof.
(* 按照顺序,首先引入变量n,其次为m *)
intros n m.
(* 按照顺序,接着引入n = m的假设条件 *)
intros H.
...
If there are universally quantified variables in the goal (i.e.
forall
), you can introduce those variables into the context using theintros
tactic. You can also useintros
to introduce all propositions on the left side of an implication as assumptions.
reflexivity
If the goal contains an equality that looks obviously true, the
reflexivity
tactic can finish off the proof, doing some basic simplification if needed.
rewrite
使用假设中的关系重写目标等式。
Theorem t1 : forall n m : nat, n = m -> n + n = m + m.
Proof.
intros n m.
intros H.
(* 使用假设H来重写目标n+n = m+m,其中->表示匹配假设H中等式的左端并重写为其右端, <-则相反*)
rewrite -> H.
...
destruct
使用destruct
对变量进行分类讨论
Theorem plus_1_neq_0 : ∀ n : nat,
(n + 1) =? 0 = false.
Proof.
intros n. destruct n as [| n'] eqn:E.
- reflexivity.
- reflexivity.
Qed.
Admitted
不进行证明,告诉coq此目标是正确的。多用于先定义小目标,利用小目标在顶层证明最终目标,然后再分别证明小目标。
函数式编程语言
OCaml, Scheme, Haskell