Basic
每次我们使用
Inductive
来声明数据类型时,Coq 会自动为这个类型生成 归纳原理。 Every time we declare a newInductive
datatype, Coq automatically generates an induction principle for this type.
自然数的归纳原理:
代码语言:javascript复制Check nat_ind. :
∀ P : nat → Prop,
P 0 →
(∀ n : nat, P n -> P (S n)) →
∀ n : nat, P n
written as inference rule:
代码语言:javascript复制 P 0
∀ n : nat, P n -> P (S n)
-------------------------
∀ n : nat, P n
induction
tactic is wrapper ofapply t_ind
Coq 为每一个Inductive
定义的数据类型生成了归纳原理,包括那些非递归的 Coq generates induction principles for every datatype defined withInductive
, including those that aren’t recursive. 尽管我们不需要使用归纳来证明非递归数据类型的性质 Although of course we don’t need induction to prove properties of non-recursive datatypes. (destruct
would be sufficient) 归纳原理的概念仍然适用于它们: 它是一种证明一个对于这个类型所有值都成立的性质的方法。 the idea of an induction principle still makes sense for them: it gives a way to prove that a property holds for all values of the type.
Non-recursive
代码语言:javascript复制Inductive yesno : Type :=
| yes
| no.
Check yesno_ind. :
yesno_ind : ∀ P : yesno → Prop,
P yes →
P no →
∀ y : yesno, P y
代码语言:javascript复制 P yes
P no
------------------
∀ y : yesno, P y
Structural-Recursive
代码语言:javascript复制Inductive natlist : Type :=
| nnil
| ncons (n : nat) (l : natlist).
Check natlist_ind. :
natlist_ind : ∀ P : natlist → Prop,
P nnil →
(∀ (n : nat) (l : natlist), P l -> P (ncons n l)) →
∀ l : natlist, P l
代码语言:javascript复制 P nnil
∀ (n : nat) (l : natlist), P l -> P (ncons n l)
-----------------------------------------------
∀ l : natlist, P l
P
only need to fullfill l : the_type
but not n:nat
since we are proving property of the_type
.
The Pattern
These generated principles follow a similar pattern.
- induction on each cases
- proof by exhaustiveness?
Inductive t : Type :=
| c1 (x1 : a1) ... (xn : an)
...
| cn ...
t_ind : ∀P : t → Prop,
... case for c1 ... →
... case for c2 ... → ...
... case for cn ... →
∀n : t, P n
对于 t
的归纳原理是又所有对于 c
的归纳原理所组成的: (即所有 case 成立)
对于 c
的归纳原理则是
对于所有的类型为
a1...an
的值x1...xn
,如果P
对每个 归纳的参数(每个具有类型t
的xi
)都成立,那么P
对于c x1 ... xn
成立”
每个具有类型 t
的参数的地方即发生了「递归」与「子结构」,归纳假设 = 「对子结构成立」.
Polymorphism
接下来考虑多态列表:
代码语言:javascript复制(* in ADT syntax *)
Inductive list (X:Type) : Type :=
| nil
| cons (x : X) (l': list X)
(* in GADT syntax *)
Inductive list (X:Type) : Type :=
| nil : list X
| cons : X → list X → list X.
here, the whole def is parameterized on a
set X
: that is, we are defining a family of inductive typeslist X
, one for eachX
.
这里,整个定义都是被集合 X
参数化的:
也即,我们定义了一个族 list : X -> Type
, 对于每个 X
,我们都有一个对应的项: list X
, which is a Type
, 可写作 list X : Type
.
list_ind
can be thought of as a polymorphic function that, when applied to a typeX
, gives us back an induction principle specialized to the typelist X
.
因此,其归纳定理 list_ind
是一个被 X
参数化多态的函数。
当应用 X : Type
时,返回一个特化在 list X : Type
上的归纳原理
list_ind : ∀(X : Type) (P : list X → Prop),
P [] →
(∀(x : X) (l : list X), P l → P (x :: l)) →
∀l : list X, P l
代码语言:javascript复制∀(X : Type), {
P [] -- base structure holds
∀(x : X) (l : list X), P l → P (x :: l) -- sub-structure holds -> structure holds
---------------------------------------
∀l : list X, P l -- all structure holds
}
Induction Hypotheses 归纳假设
The induction hypothesis is the premise of this latter implication — the assumption that
P
holds ofn'
, which we are allowed to use in proving thatP
holds forS n'
.
归纳假设就是 P n' -> P (S n')
这个蕴含式中的前提部分
使用 nat_ind
时需要显式得用 intros n IHn
引入,于是就变成了 proof context 中的假设.
More on the induction
Tactic
“Re-generalize” 重新泛化
Noticed that in proofs using nat_ind
, we need to keep n
generailzed.
if we intros
particular n
first then apply nat_ind
, it won’t works…
But we could intros n. induction n.
, that’s induction
tactic internally “re-generalize” the n
we perform induction on.
Automatic intros
i.e. specialize variables before the variable we induction on
A canonical case is induction n
vs induction m
on theorem plus_comm'' : ∀n m : nat, n m = m n.
.
to keep a var generial…we can either change variable order under ∀
, or using generalize dependent
.
Induction Principles in Prop
理解依赖类型的归纳假设 与 Coq 排除证据参数的原因
除了集合 Set
,命题 Prop
也可以是归纳定义与 induction
on 得.
难点在于:Inductive Prop 通常是 dependent type 的,这里会带来复杂度。
考虑命题 even
:
Inductive even : nat → Prop :=
| ev_0 : even 0
| ev_SS : ∀n : nat, even n → even (S (S n)).
我们可以猜测一个最 general 的归纳假设:
代码语言:javascript复制ev_ind_max : ∀ P : (∀n : nat, even n → Prop),
P O ev_0 →
(∀(m : nat) (E : even m), P m E → P (S (S m)) (ev_SS m E)) →
∀(n : nat) (E : even n), P n E
即:
代码语言:javascript复制 P 0 ev_0 -- base
∀(m : nat) (E : even m), P m E → P (S (S m)) (ev_SS m E) -- sub structure -> structure
--------------------------------------------------------
∀(n : nat) (E : even n), P n E -- all structure
注意这里:
even
is indexed by natn
(对比list
is parametrized byX
)- 从族的角度:
even : nat -> Prop
, a family ofProp
indexed bynat
- 从实体角度: 每个
E : even n
对象都是一个 evidence that particular nat is even.
- 从族的角度:
- 要证的性质
P
is parametrized byE : even n
也因此连带着 byn
. 也就是P : (∀n : nat, even n → Prop)
(对比P : list X → Prop
)- 所以其实关于
even n
的性质是同时关于数字n
和证据even n
这两件事的.
- 所以其实关于
因此 sub structure -> structure
说得是:
whenever
n
is an even number andE
is an evidence of its evenness, ifP
holds ofn
andE
, then it also holds ofS (S n)
andev_SS n E
. 对于任意数字n
与证据E
,如果P
对n
和E
成立,那么它也对S (S n)
和ev_SS n E
成立。
然而,当我们 induction (H : even n)
时,我们通常想证的性质并不包括「证据」,而是「满足该性质的这 Type
东西」的性质,
比如:
nat
上的一元关系 (性质) 证明nat
的性质 :ev_even : even n → ∃k, n = double k
nat
上的二元关系 证明nat
上的二元关系 :le_trans : ∀m n o, m ≤ n → n ≤ o → m ≤ o
- 二元关系
reg_exp × list T
证明 二元关系reg_exp × T
:in_re_match : ∀T (s : list T) (x : T) (re : reg_exp), s =~ re → In x s → In x (re_chars re).
都是如此,
因此我们也不希望生成的归纳假设是包括证据的… 原来的归纳假设:
代码语言:javascript复制 ∀P : (∀n : nat, even n → Prop),
... →
∀(n : nat) (E : even n), P n E
可以被简化为只对 nat
参数化的归纳假设:
∀P : nat → Prop,
... →
∀(n : nat) (E: even n), P n
因此 coq 生成的归纳原理也是不包括证据的。注意 P
丢弃了参数 E
:
even_ind : ∀ P : nat -> Prop,
P 0 →
(∀ n : nat, even n -> P n -> P (S (S n))) →
∀ n : nat, even n -> P n *)
用人话说就是:
- P 对 0 成立,
- 对任意 n,如果 n 是偶数且 P 对 n 成立,那么 P 对 S (S n) 成立。 => P 对所有偶数成立
“General Parameter”
代码语言:javascript复制Inductive le : nat → nat → Prop :=
| le_n : ∀ n, le n n
| le_S : ∀ n m, (le n m) → (le n (S m)).
代码语言:javascript复制Inductive le (n:nat) : nat → Prop :=
| le_n : le n n
| le_S m (H : le n m) : le n (S m).
两者虽然等价,但是共同的 ∀ n
可以被提升为 typecon 的参数, i.e. “General Parameter” to the whole definition.
其生成的归纳假设也会不同: (after renaming)
代码语言:javascript复制le_ind : ∀ P : nat -> nat -> Prop,
(∀ n : nat, P n n) ->
(∀ n m : nat, le n m -> P n m -> P n (S m)) ->
∀ n m : nat, le n m -> P n m
代码语言:javascript复制le_ind : ∀ (n : nat) (P : nat -> Prop),
P n ->
(∀ m : nat, n <= m -> P m -> P (S m)) ->
∀ m : nat, n <= m -> P m
The 1st one looks more symmetric but 2nd one is easier (for proving things).