Library induction
induction
ゴール: a + 0 = a
Show Proof.
証明項: (fun a : nat => ?Goal)
induction の直前では、intros で構築した関数抽象の本体がゴールとなっています。
induction a.
ゴール1: 0 + 0 = 0
ゴール2: S a + 0 = S a
induction の直後では、a が 0 の場合のゴールと、
a が 0 でない (つまり S a の場合の) ゴールのふたつのゴールが生成されています。
Show Proof.
証明項:
証明項をみると、nat_ind という関数を呼び出しており、
引数の中には ?Goal と ?Goal0@{n:=n0} があって、
ふたつのゴールがあることがわかります。
nat_ind がどんな関数なのか About でみてみましょう。
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) ?Goal (fun (a0 : nat) (IHa : a0 + 0 = a0) => ?Goal0@{a:=a0}) a)
nat_ind : forall P : nat -> Prop, P 0 -> (forall n : nat, P n -> P (S n)) -> forall n : nat, P n
nat_ind : forall (P : forall _ : nat, Prop) (_ : P O) (_ : forall (n : nat) (_ : P n), P (S n)) (n : nat), P n
- 第1引数 P として nat を受け取って命題を返す関数を受けとります。
- 第2引数に P 0 の証明を受け取ります。
- 第3引数に n と P n の証明を受け取って P (S n) の証明を返す関数を受けとります。
- 第4引数に n を受けとります。
- そして、P n の証明を返します。
- 第1引数: (fun a0 : nat => a0 + 0 = a0)
- 第2引数: ?Goal
- 第3引数: (fun (a0 : nat) (IHa : a0 + 0 = a0) => ?Goal0@{a:=a0})
- 第4引数: a
reflexivity.
Show Proof.
Show Proof.
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl (fun (a0 : nat) (IHa : a0 + 0 = a0) => ?Goal@{a:=a0}) a)
a : nat IHa : a + 0 = aそして、S a + 0 = S a というゴールを証明しなければなりません。
simpl.
Show Proof.
Show Proof.
ゴール: S (a + 0) = S a
証明項:
simpl では計算を進めるだけで、そういう計算でたどり着ける項は等しいと考えるので、
証明項は変わりません。
simpl の結果、ゴール内に a + 0 という IHa で書き換えられる部分が出てきたので書き換えます。
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl (fun (a0 : nat) (IHa : a0 + 0 = a0) => ?Goal@{a:=a0}) a)
rewrite IHa.
Show Proof.
Show Proof.
ゴール: S a = S a
証明項:
rewrite により、eq_ind_r の呼び出しが生成され、ゴールは S a = S a に変化します。
このゴールは両辺が等しいので、reflexivity で証明できます。
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl (fun (a0 : nat) (IHa : a0 + 0 = a0) => eq_ind_r (fun n : nat => S n = S a0) ?Goal@{a:=a0} IHa) a)
reflexivity.
Show Proof.
Show Proof.
証明項:
ゴールに eq_refl が埋まって、ゴールがなくなったのでこれで証明は終了です。
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl (fun (a0 : nat) (IHa : a0 + 0 = a0) => eq_ind_r (fun n : nat => S n = S a0) eq_refl IHa) a)
Qed.
induction が nat_ind を呼び出す関数呼び出しを構築することがわかりました。
そこで、induction ではなく、apply で同じ証明項を構築してみましょう。
induction a とするかわりに、(nat_ind (fun a0 : nat => a0 + 0 = a0)) という
関数の呼び出しを構築します。
第1引数を指定しているのは、指定しない場合に Coq が推論する項はこれとは異なる項になってしまうからです。
指定しない場合については後でみてみましょう。
ゴール1: 0 + 0 = 0
ゴール2: forall n : nat, n + 0 = n -> S n + 0 = S n
証明項: (fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) ?Goal ?Goal0 a)
期待通り nat_ind の呼び出しが構築されています。
induction とは異なり、第3引数の関数抽象は構築されていませんが、
apply は関数呼び出しを構築するものですからしょうがないでしょう。
最初のゴールは reflexivity で証明できます。
reflexivity.
Show Proof.
Show Proof.
証明項: (fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl ?Goal a)
induction による証明項と同じ証明項を作るには、
つぎのゴールに関数抽象を作る必要があります。
これは intros で可能ですが、ひとつ問題なのは前提にすでに a が存在するため、
intro a がエラーになることです。
Fail intro a.
そこで、まず前提の a を消去します。
これには clear を使います。
clear a.
Show Proof.
Show Proof.
証明項: (fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) eq_refl ?Goal a)
clear が前提から消去するのは、束縛された変数を前提としてアクセスできなくするというだけで、
証明項は変わりません。
これで前提が空になったので、intros a IHa として関数抽象を2段階構築します。
intros a IHa.
Show Proof.
Show Proof.
(fun a : nat => nat_ind (fun a0 : nat => a0 + 0 = a0) (@eq_refl nat 0) (fun (a0 : nat) (IHa : a0 + 0 = a0) => ?Goal@{a:=a0}) a)
simpl.
rewrite IHa.
reflexivity.
Qed.
rewrite IHa.
reflexivity.
Qed.
apply nat_ind とだけ指定した場合にどうなるかみてみましょう。
ゴール1: a + 0 = 0
ゴール2: forall n : nat, a + 0 = n -> a + 0 = S n
証明項: (fun a : nat => nat_ind (@eq nat (a + 0)) ?Goal ?Goal0 a)
ゴール1をみるとこれはあからさまに証明できないので、なにか間違えたことがわかります。
証明項をみると、第1引数P には (@eq nat (a + 0)) が渡されています。
eq の型をみてみましょう。
eq : forall A : Type, A -> A -> Prop
Abort.