更多内容见 https://github.com/january147/software_foundations_notes
pair和多元素匹配
(a, b)
作为一个pair
表示一个变量,在match
的匹配列表左端,多个由逗号分隔但没有在括号里的变量表示匹配多个变量
(* Can't match on a pair with multiple patterns: *)
Definition bad_fst (p : natprod) : nat :=
(* match仅匹配一个变量p *)
match p with
(* 匹配规则定义了两个变量x和y,因此无法正常运行 *)
| x, y ⇒ x
end.
包含在一个括号内的多个使用逗号分隔的变量表示匹配一个变量,括号内的各个变量表示匹配整体变量内部的各个子变量模式
(* Can't match on multiple values with pair patterns: *)
Definition bad_minus (n m : nat) : nat :=
(* match要求匹配两个变量n和m *)
match n, m with
(* 匹配规则仅定义了一个变量,因此无法正常运行 *)
| (O , _ ) ⇒ O
| (S _ , O ) ⇒ n
| (S n', S m') ⇒ bad_minus n' m'
end.
Search
使用Search
命令可以搜索之前已经定义和证明的定理。例如如下命令可以搜索Basics
模块中所有使用了plus
的定理
Search plus inside Basics.
使用如下命令可以搜索到包含(_ + _ = _ + _)
模式的定理
Search (_ + _ = _ + _)
option
如果我们需要编写一个返回列表中第n
个元素的函数,当n
大于列表长度时需要一个能够表示错误的返回结果,可以定义如下类型作为返回结果。在有合法结果时返回合法结果,没有合法结果则返回一个特殊的表示错误的值。
Inductive natoption : Type :=
| Some (n : nat)
| None.
如下是使用上述类型作为返回结果的获取列表中第n
个元素的函数。
Fixpoint nth_error' (l:natlist) (n:nat) : natoption :=
match l with
| nil => None
| a :: l' => if n =? O then Some a
else nth_error' l' (pred n)
end.
该函数中没有使用match
而使用了if
语句。if
可以视为一种针对特定类型的match
, 由于coq
没有定义内置的bool
类型,因此if
接受任何仅使用两个construct
定义的类型的值,例如如下所示的T
类型
Inductive T : Type :=
| A
| B
其中由第一个construct
(例如A
)定义的值被匹配到then
对应的表达式(即表示true
), 由第二个constructor
定义的值(例如B
)被匹配到else
对应的表达式(即表示false
)。
重要练习
交替组合两个List
coq的Fixpoint
要求必须在一个参数上递减,因此直接使用match l1, l2
匹配两个参数进行递归定义是无法通过。(即下面的定义无法通过),因为对两个参数同时进行递归定义会导致coq无法判定递归的终止性。
Fixpoint alternate (l1 l2 : natlist) : natlist :=
match l1,l2 with
| nil, nil => nil
| nil, h :: t => h :: (alternate nil t)
| h :: t, nil => h :: (alternate t nil)
| h1 :: t1, h2 :: t2 => h1 :: h2 :: (alternate t1 t2)
end.
需要使用嵌套match
首先在第一个参数上进行递减的递归定义,使coq能够确定该定义的终止性,然后再使用match
处理第二个参数。
Fixpoint alternate (l1 l2 : natlist) : natlist :=
match l1 with
| nil => l2
| h1 :: t1 => match l2 with
| nil => l1
| h2 :: t2 => h1 :: h2 :: (alternate t1 t2)
end
end.
列表逆序函数的单射性质
要证明的目标为rev l1 = rev l2 -> l1 = l2
,可以看到该目标中假设部分rev l1 = rev l2
包含了rev
函数运算而目标部分没有运算,而我们目前学的tactics
都是针对目标进行变换和操作,因此这个假设难以被用于证明目标。为了证明目标,我们需要对目标进行变化,使其包含rev
函数,因而能够使用假设。根据rev_involutive
定理forall l : natlist, rev (rev l) = l
,我们可以将目标中的l1
或者l2
转换为rev (rev l1)
或者rev (rev l2)
, 因此将rev
函数引入了目标,就能够使用定理中的假设条件进行变换和证明了。
Theorem rev_injective : forall (l1 l2 : natlist),
rev l1 = rev l2 -> l1 = l2.
Proof.
intros l1 l2 H.
rewrite <- rev_involutive.
rewrite <- H.
rewrite -> rev_involutive.
reflexivity.
Qed.