■
Coq。証明中、Undo機能が欲しいなーと思ってたら、ちゃんとあった。前回の補足のような。Variableで大域変数を宣言したけど、
Coq < forall A B C:Prop , (A->B->C)->(A->B)->(A->C).
みたく、forallでローカルに束縛しちゃっていいらしい。Propが型というのは多少変な感じだけど、Propを返り値にした関数もつくれる。例えば、集合AとA上の二項演算opが半群である(つまり、opが結合律を満たす)ことを意味する命題を返す関数。
Definition Semigroup := fun (A:Set) => fun (op:A->A->A) => forall x y z:A , op x (op y z) = op (op x y) z.
Coq < Goal (Semigroup nat plus). Unnamed_thm2 < compute.
やたら長い式が出力されるけど、以下、普通に、結合律の証明をやっていけば証明できる。
それから、関数定義で、if文ないと不便すぎる("match ... with ..."ではinductiveでないデータ型には使えないし)と思っていたら、一応あるらしいけど、
Coq < Definition bool_to_int := fun (x:Bool) => if x then 0 else 1.
で。then elseのタイプがめんどい。しかし、(x<3)とかの型は、Boolではなく、Propなので、どうすればいいんだ。gcd関数を普通に定義できない気が。
前回挫折した1+...+n=n*(n+1)/2の証明
Fixpoint sum (n:nat) {struct n} : nat := match n with | O => O | (S p) => (S p)+(sum p) end. Coq < Theorem sum_of_1_to_n: forall x:nat , 2*(sum x)=x*(x+1). sum_of_1_to_n < induction x;auto. 1 subgoal x : nat IHx : 2 * sum x = x * (x + 1) ============================ 2 * sum (S x) = S x * (S x + 1) sum_of_1_to_n < Lemma sub:forall n:nat,sum (S n)=(S n)+(sum n). sub < auto. sub < Qed. sum_of_1_to_n < rewrite sub. sum_of_1_to_n < Require Import Arith. sum_of_1_to_n < rewrite mult_plus_distr_l. sum_of_1_to_n < rewrite IHx. sum_of_1_to_n < Require Import ArithRing. sum_of_1_to_n < ring_nat. sum_of_1_to_n < Qed.
subはsum (S n)=(S n)+(sum n)という定義から自明な補題で、これを使って、2*(sum (S x))を2*( (sum x)+(S x) )に置き換える。mult_plus_distr_lは、x*(y+z)=x*y+x*zというLemmaで、2*( (sum n)+(S n) )を展開するこの補題はArithというライブラリで証明されている。"rewrite IHx"は、仮定IHxを使って下式の左辺の2*(sum x)をx*(x+1)に書き換える。この時点で、下式は、xの多項式の比較になってるので、両辺を正規形に変形してやれば、等しいことを自動的に証明できるはず。多項式を正規形に変形するのが、Ring tacticというもの。けど、下式は、Sを含むので、(S x)をx+1に置き換えてやる必要がある。これも含めて自動的にやってくれるのが、ring_natというtacticマクロで、"rewrite S_to_plus_one; ring. "を繰り返すらしい。これはArithRingで定義されている。最後のring_natなしで、手でやろうとすると、結構大変なことになる
色々分からないけど、算術は飽きたので、リスト。標準ライブラリで色々定義されているようなので、それを使う
Coq < Require Import List. Coq < Check cons. cons : forall A : Set, A -> list A -> list A Coq < Check list. list : Set -> Set Coq < Eval compute in (cons 3 (cons 2 nil)). = 3 :: 2 :: nil : list nat Coq < Eval compute in (length (cons 3 (cons 2 nil))). = 2 : nat
とりあえず、練習。
Coq < Goal forall (S:Set) (x y:list S) , (length (app x y)) = (length x) + (length y). (中略) Unnamed_thm0 < Qed. intros. induction x. simpl in |- *; trivial. simpl in |- *. rewrite IHx. trivial. Unnamed_thm2 is defined
Coq < Goal forall (S T:Set) (f:S->T) (x y:list S) , Coq < (map f (app x y)) = (app (map f x) (map f y)). (中略) Unnamed_thm1 < Qed. intros. induction x. simpl in |- *. trivial. simpl in |- *. rewrite IHx. trivial. Unnamed_thm1 is defined
練習なので、autoで済むところも、敢えて手でやっている。基本的に帰納法。
まあ、Coqは何となく分かってきた。とりあえず、(定理|補題)名が分かりづらい。これ覚えれる気はしない。最初の方に書いたような命題をパラメータ化するような手法がほしいね。結合律だったら、Associative(nat,plus)とか、Associative(list,app)みたいに書けるといい。あと、推論エンジンが馬鹿っぽいというか、例えばappの結合律は、app_assとass_appと二種類あるとか。
やってることといえば、H0,... |- G0,...という図をどんどん変換していく関数みたいなんがtacticで、|- Goalから始めて、H0,...|-という形に変形できれば終わりみたいな、そんな感じ。多分。いや、それだと型理論とかはどこいったって話なんだけど。あと、CoqはTuring-completeではないという話が。むう。けど、それだとTuring completeな言語のformal semantics書いたりできないような。いや、書けるけど、計算できないのか。よく分かんないな