26 April, 2010

[Coq] Tail-recursive reverse

 先のreverse (reverse xs) = xsで示した reverse は末尾再帰でなく、効率の悪い定義である。
 そこで末尾再帰な
Fixpoint rev' {A:Set} (xs ys:list A) :=
match xs with
| nil => ys
| cons x xs' => rev' xs' (cons x ys)
end.
Definition rev {A:Set} (xs:list A) :=
rev' xs nil.
Eval compute in rev (cons 1 (cons 2 (cons 3 nil))).

について
Theorem reverse_rev : forall (A:Set) (xs:list A),
reverse xs = rev xs.

が成り立つ事を示そう。まず補題
Lemma rev'_reverse : forall (A:Set) (xs ys:list A),
rev' xs ys = reverse xs ++ ys.
Proof.
intros A xs.
induction xs; simpl.
intro. reflexivity.
intro ys'.
rewrite (IHxs (cons a ys')).
(* reverse xs ++ cons a ys' =
(reverse xs ++ cons a nil) ++ ys' *)
assert (H: cons a ys' = (cons a nil) ++ ys').
induction ys'; simpl.
reflexivity.
reflexivity.
rewrite H.
rewrite (append_assoc A (reverse xs) (cons a nil) ys').
reflexivity.
Qed.

である。証明の冒頭で intros A xs だけを行い、ysについては intro していないが、ys を残したまま xs の帰納法を行わないと、IHxs を apply するときにうまくいかない。「無闇に intro しない方が良い」という良い例なので、ys を一緒に intros してしまうなど自分で試行錯誤してみて欲しい。

 この補題を使えば、
Theorem reverse_rev : forall (A:Set) (xs:list A),
reverse xs = rev xs.
Proof.
intros.
unfold rev.
induction xs; simpl.
reflexivity.
(* reverse xs ++ cons a nil = rev' xs (cons a nil) *)
rewrite IHxs.
(* rev' xs nil ++ cons a nil = rev' xs (cons a nil) *)
rewrite (rev'_reverse A xs nil).
rewrite (rev'_reverse A xs (cons a nil)).
assert (H: forall l:list A, l ++ nil = l).
induction l; simpl.
reflexivity.
rewrite IHl. reflexivity.
rewrite (H (reverse xs)).
reflexivity.
Qed.

と証明出来る。

[Coq] reverse (reverse xs) = xs

 先日のProofCafeでみんなで解いていた reverse (reverse xs) = xs の証明について、どんな風に解くかを解説というか自分の解答を晒すというか。

 教科書でこの問題を演習課題として解く場合は、予め必要な補題が順々に証明課題として与えられる事が多いが、現実の問題ではそういうことはまず無い。(このあたり、現実の問題と試験問題の違いにも通ずる話だ。)

 まず、ProofCafe01から必要な定義をコピーしよう。
Inductive list (A: Type) : Type :=
| nil : list A
| cons : A -> list A -> list A.

Implicit Arguments nil [A].
Implicit Arguments cons [A].

Fixpoint append {A : Type} (xs ys: list A) : list A :=
match xs with
| nil => ys
| cons x xs => cons x (append xs ys)
end.
Infix "++" := append (at level 60).

Theorem append_assoc : forall (A:Type) (xs ys zs:list A),
(xs ++ ys) ++ zs = xs ++ (ys ++ zs).
Proof.
(* あなたの証明を書いてね *)
Qed.

定理append_assocの証明は難しく無いと思うが、判らない人はProofCafeのページを参照して欲しい。

 次いで、関数reverseを定義しよう。
 関数型言語で関数を末尾再帰で書くのに慣れている人は、ついうっかり
Fixpoint rev' {A:Set} (xs ys:list A) :=
match xs with
| nil => ys
| cons x xs' => rev' xs' (cons x ys)
end.
Definition rev {A:Set} (xs:list A) := rev' xs nil.

と書いてしまうだろう。私も実は当日そう書いてしまい嵌った。

 ここは実行効率を考えず、ひとまず素直に、
Fixpoint reverse {A:Set} (xs:list A) :=
match xs with
| nil => nil
| cons x xs' => (reverse xs') ++ (cons x nil)
end.

と定義して証明する方が簡単だ。関数を実際に動かしてみる場合は、
Eval compute in reverse (cons 1 (cons 2 (cons 3 nil))).

とか入力してみれば良い。

 ここから、どうやって証明していったか、考えた過程を示そう。
 まずはいきなり、定理を証明しようとしてみた。
Theorem reverse_reverse : forall (A:Set) (xs:list A),
reverse (reverse xs) = xs.
Proof.
induction xs; simpl.
reflexivity.

最初にxsについての帰納法を試み、xs=nilのケースは簡単に証明出来た。次のゴールの
reverse (reverse xs ++ (cons a nil) = cons a xs
については、リストを ++ で繋いでreverseする、
Hypothesis reverse_append : forall (A:Set) (xs ys:list A),
reverse (xs ++ ys) = (reverse ys) ++ (reverse xs).

という定理があれば都合が良さそうだと何となく思いつく。(なんとなく美しげな定理だし。)

 そこで、とりあえず上記のHypothesisを先に定義して、改めてreverse_reverseを証明する。
Theorem reverse_reverse : forall (A:Set) (xs:list A),
reverse (reverse xs) = xs.
Proof.
induction xs; simpl.
reflexivity.
rewrite (reverse_append A (reverse xs) (cons a nil)).
rewrite IHxs.


 この時点でゴールが
reverse (cons a nil) ++ xs = cons a xs
となる。そこで再度
Hypothesis append_cons_nil : forall (A:Set) (a:A) (xs:list A),
(cons a nil) ++ xs = cons a xs.
Hypothesis reverse_cons : forall (A:Set) (a:A),
reverse (cons a nil) = cons a nil.

を追加してから、改めてreverse_reverseを証明する。
Theorem reverse_reverse : forall (A:Set) (xs:list A),
reverse (reverse xs) = xs.
Proof.
induction xs; simpl.
reflexivity.
rewrite (reverse_append A (reverse xs) (cons a nil)).
rewrite IHxs.
rewrite (reverse_cons A a).
rewrite (append_cons_nil A a xs).
reflexivity.
Qed.


 証明が出来たので、Hypothesisで誤摩化していた部分をLemma, Theoremに書き換えて証明する。
Lemma append_cons_nil : forall (A:Set) (a:A) (xs:list A),
(cons a nil) ++ xs = cons a xs.
Lemma reverse_cons : forall (A:Set) (a:A),
reverse (cons a nil) = cons a nil.

については難しく無いので証明してみると良いだろう。(intros, simpl, reflexivityで証明出来る。)

Theorem reverse_append : forall (A:Set) (xs ys:list A),
reverse (xs ++ ys) = (reverse ys) ++ (reverse xs).

については、解説しよう。

Proof.
intros A xs ys.
induction xs; simpl.
assert (append_nil: forall (zs:list A), zs = zs ++ nil).
induction zs; simpl.
reflexivity.
rewrite <- IHzs. reflexivity.
apply (append_nil (reverse ys)).
rewrite IHxs.
rewrite (append_assoc A (reverse ys) (reverse xs) (cons a nil)).
reflexivity.
Qed.

証明の途中でzs = zs ++ nilが使いたくなり、わざわざ補題にするまでもないと思って、途中でassertで証明している。
 この証明したreverse_appendを使って、reverse_reverseを証明すれば完成。

 全体を改めて示すとこのようになる。
Inductive list (A: Type) : Type :=
| nil : list A
| cons : A -> list A -> list A.

Implicit Arguments nil [A].
Implicit Arguments cons [A].

Fixpoint append {A : Type} (xs ys: list A) : list A :=
match xs with
| nil => ys
| cons x xs => cons x (append xs ys)
end.
Infix "++" := append (at level 60).

Theorem append_assoc : forall (A: Type) (xs ys zs : list A),
(xs ++ ys) ++ zs = xs ++ (ys ++ zs).
Proof.
intros.
induction xs; simpl.
reflexivity.
rewrite IHxs. reflexivity.
Qed.

Fixpoint reverse {A:Set} (xs:list A) :=
match xs with
| nil => nil
| cons x xs' => (reverse xs') ++ (cons x nil)
end.

Lemma append_cons_nil : forall (A:Set) (a:A) (xs:list A),
(cons a nil) ++ xs = cons a xs.
Proof.
intros. simpl. reflexivity.
Qed.
Lemma reverse_cons : forall (A:Set) (a:A),
reverse (cons a nil) = cons a nil.
Proof.
intros. simpl. reflexivity.
Qed.

Theorem reverse_append : forall (A:Set) (xs ys:list A),
reverse (xs ++ ys) = (reverse ys) ++ (reverse xs).
Proof.
intros A xs ys.
induction xs; simpl.
assert (append_nil: forall (zs:list A), zs = zs ++ nil).
induction zs; simpl.
reflexivity.
rewrite <- IHzs. reflexivity.
apply (append_nil (reverse ys)).
rewrite IHxs.
rewrite (append_assoc A (reverse ys) (reverse xs) (cons a nil)).
reflexivity.
Qed.

Theorem reverse_reverse : forall (A:Set) (xs:list A),
reverse (reverse xs) = xs.
Proof.
induction xs; simpl.
reflexivity.
rewrite (reverse_append A (reverse xs) (cons a nil)).
rewrite IHxs.
rewrite (reverse_cons A a).
rewrite (append_cons_nil A a xs).
reflexivity.
Qed.

[Coq] Proof Cafe #01

Proof Cafe (栄)に参加しました。

 当日使われた資料はyoshihiro503の日記を参照の事。

 yoshihiro503さんによるCoq最速文法マスターという感じで、僅か90分の入門講座で、(Haskellなど関数型言語の知識があるとはいえ)Coqに初めての人が
Theorem append_length : forall (A: Type) (xs ys: list A),
length (xs ++ ys) = length xs + length ys.

を証明出来る様になるってのは、やはり説明の手際が良いよなぁ。次に入門用PPTを修正する時は是非とも参考にしよう。

 Proof Cafe自体は2時間だったんですが、その後、懇親会を実施して頂き、なんか身に余る様な歓待をして頂きました。皆さんどうもありがとうございました。Coqに限らず関数型言語とかIT業界の話とか色々楽しく話をしました。

 Proof Cafeは今後も毎月名古屋で開催されるとか、Proof CafeでもCPDTを読もうとしている、ようです。Formal Methods Forumの方でも頑張ってCPDTを読んでいきたいです。

 

18 April, 2010

[Coq] Install on NetWalker

 買ってあったがしばらく放置していたNetWalkerにCoq, CoqIDEをインストールしました。まぁUbuntuなんで、sudo apt-get install coq coqideでインストール出来て当然だが、なんかCoqのバージョンが古いみたい。
 ともあれ通勤途中にCoqで遊べる様になった。

17 April, 2010

[Coq][FM] Formal Methods Forum #4 on 4/29

 形式仕様に関する勉強会のATND - 第4回FormalMethods勉強会を4/29に行います。
 Coqに関してはCerti􏰀ed Programming with Dependent Typesという教科書を今回から読み進める予定です。この本は、関数型言語のプログラマ向けに書かれた割と実践的な教科書です。
 Coq以外の内容についてはATNDのページからFormal Methods Forumのページを辿って下さい。

[Haskell] Haskellers Meeting 2010 Spring

Haskellers Meeting 2010 Springを聞きにいきました。

 和田先生の話は、まぁ割とどうでも良い昔話だった。
 メインはSimon Peyton JonesさんのSTMの話。基本的には"Beautiful Code"に載っている話で、ジョークなども比較的聞き取りやすかった。ScalaにもSTM早く欲しいなぁ。
 山本さんのHaskellでWebサーバの話は前に聞いたことのある話だった。
 山下さんの擬データの話が、実は一番興味深かった。紹介された論文「擬データを用いた対話的関数プログラミングに関する研究」(石井裕一郎)はWeb上で見つからなかったが、「擬データと関数による並行プロセス群の記述」は検索するとCiNii上で読める様だ。擬データは興味深いのだけど、Haskell以外の言語では意味が無いかなぁ。

----
追記:
山下さんの発表資料はここから入手可能。

03 April, 2010

[Coq][FM] Formal Methods Forum Meeting #3

一見、キャンセル待ち状況になっている第3回FormalMethods勉強会ですが、まぁ椅子を追加して対応可能だと思うので、とりあえず名前をATNDに書いて下さい。

あと、直前に案内メールとかが流れますのでFormalMethods勉強会のGoogle groupにも登録して頂くと良いと思います。