12 December, 2010

[Coq] Coq Advent Calender : replace (12 of 25)

Coq でよく使われる tactic の12番目は replace です。

 replace t1 with t2 とすると、t2 = t1 という等式をsubgoalに追加し、t2 = t1 を用いてゴールの書き換えを行います。書き換え規則 t2 = t1 の証明を先送りにすることで本筋の証明の見通しがよくなります。また、simpl, unfold, rewriteを使う場合とかで、「ゴールのこの部分だけ書き換えたいのだが別のところも書き換えられてしまう」と悩む場面もありますが、そういうときは replace でとりあえず部分的に書き換え、あとでその箇所の書き換えを証明すると楽です。

 replace を多用するのは、等式変形を繰り返す場合です。今回はそういう例を。
 下記の様に群 G を定義してみます。

Coq < Axiom G : Set. (* 群G *)
Coq < Axiom G_dec : forall a b:G, {a=b} + {a <> b}. (* 単位元や逆元の一意性とかを示す時に必要 *)
Coq < Axiom mult : G -> G -> G. (* 乗法 *)
Coq < Notation "a * b" := (mult a b). (* 記号 * で書ける様にする *)
Coq < Axiom assoc : forall a b c:G, (a * b) * c = a * (b * c). (* 結合則 *)
Coq < Axiom G1 : G. (* 単位元 *)
Coq < Notation "1" := G1. (* 記号 1 で書ける様にする *)
Coq < Axiom id_l : forall a:G, 1 * a = a. (* 左単位元である *)
Coq < Axiom inv : G -> G. (* 逆元 *)
Coq < Axiom inv_l : forall a:G, (inv a) * a = 1. (* 左逆元 *)


 では、左逆元は右逆元でもあることを証明してみます。長くなるのでゴールを示すのは要所要所だけです。replaceを使うと、subgoalが増えているのが判ります。

Coq < Theorem inv_r : forall a:G, a * (inv a) = 1.
inv_r < intros.
1 subgoal

a : G
============================
a * inv a = 1

inv_r < replace (a * inv a) with (1 * a * inv a).
2 subgoals

a : G
============================
1 * a * inv a = 1

subgoal 2 is:
1 * a * inv a = a * inv a

inv_r < replace (1 * a * inv a) with ((inv (inv a) * (inv a)) * a * inv a).
3 subgoals

a : G
============================
inv (inv a) * inv a * a * inv a = 1

subgoal 2 is:
inv (inv a) * inv a * a * inv a = 1 * a * inv a
subgoal 3 is:
1 * a * inv a = a * inv a

inv_r < replace (inv (inv a) * inv a * a * inv a) with (inv (inv a) * (inv a * a) * inv a).
4 subgoals

a : G
============================
inv (inv a) * (inv a * a) * inv a = 1

subgoal 2 is:
inv (inv a) * (inv a * a) * inv a = inv (inv a) * inv a * a * inv a
subgoal 3 is:
inv (inv a) * inv a * a * inv a = 1 * a * inv a
subgoal 4 is:
1 * a * inv a = a * inv a

inv_r < rewrite inv_l. (* ここからは replace を使わなくても rewrite で簡単に変形出来る *)
inv_r < rewrite assoc.
inv_r < rewrite id_l.
inv_r < rewrite inv_l.
4 subgoals

a : G
============================
1 = 1

subgoal 2 is:
inv (inv a) * (inv a * a) * inv a = inv (inv a) * inv a * a * inv a
subgoal 3 is:
inv (inv a) * inv a * a * inv a = 1 * a * inv a
subgoal 4 is:
1 * a * inv a = a * inv a

inv_r < reflexivity. (* メインゴールの証明完了。残りは replace の書き換えの証明 *)
3 subgoals

a : G
============================
inv (inv a) * (inv a * a) * inv a = inv (inv a) * inv a * a * inv a

subgoal 2 is:
inv (inv a) * inv a * a * inv a = 1 * a * inv a
subgoal 3 is:
1 * a * inv a = a * inv a

inv_r < rewrite <- assoc; reflexivity.
2 subgoals

a : G
============================
inv (inv a) * inv a * a * inv a = 1 * a * inv a

subgoal 2 is:
1 * a * inv a = a * inv a

inv_r < erewrite inv_l; reflexivity.
1 subgoal

a : G
============================
1 * a * inv a = a * inv a

inv_r < rewrite assoc; rewrite id_l; reflexivity.
Proof completed.

inv_r < Qed.
intros.
replace (a * inv a) with (1 * a * inv a) .
replace (1 * a * inv a) with (inv (inv a) * inv a * a * inv a) .
replace (inv (inv a) * inv a * a * inv a) with
(inv (inv a) * (inv a * a) * inv a) .
rewrite inv_l in |- *.
rewrite assoc in |- *.
rewrite id_l in |- *.
rewrite inv_l in |- *.
reflexivity.

rewrite <- assoc in |- *; reflexivity.

erewrite inv_l in |- *; reflexivity.

rewrite assoc in |- *; rewrite id_l in |- *; reflexivity.

inv_r is defined

Coq <

11 December, 2010

[Coq] Coq Advent Calender : exists (11 of 25)

 Coq でよく使われる tactic の11番目は exists です。
 証明のゴールが exists x, P x とか { n:nat | isPrime n } とか、ある性質を満たす要素が存在することを求めている時に、「具体的に」その要素を与えてゴールを変形します。

 下記は exists を使う簡単な例です。mの具体的な値としてS n = n+1 を与えています。具体的なmを何か与えないとomegaでの自動証明は通りません。

Coq < Require Import Omega.

Coq < Lemma Sample_of_exists : forall n, exists m, n < m.
1 subgoal

============================
forall n : nat, exists m : nat, n < m

Sample_of_exists < intros.
1 subgoal

n : nat
============================
exists m : nat, n < m

Sample_of_exists < exists (S n).
1 subgoal

n : nat
============================
n < S n

Sample_of_exists < omega.
Proof completed.

Sample_of_exists < Qed.
intros.
exists (S n).
omega.

Sample_of_exists is defined
Coq <

10 December, 2010

[TAPL][OCaml] Untyped Lambda Calculus Interpreter

 TAPLのChap.5を読むのに型無しλ計算のインタプリタが無いかなと探していたところ下記を見つけました。(...まぁそもそもそのくらい自分で作れよ、という話はあるんだが)

http://www.cs.ru.nl/~freek/notes/lambda.ml

ページの内容を lambda.ml として保存し、

$ ocaml
# #use "lambda.ml";;

で読み込まれるので、あとは下記の様に試すだけ。

# nf "(^x.fx)a";;
- : term = term "fa"
# nf "(KISS)(KISS)";;
- : term = term "^yzz'.zz'(yzz')"
# red_gk all "SKK";;
- : term list =
[term "SKK"; term "(^yz.Kz(yz))K"; term "^z.(^y.z)(Kz)"; term "^z.z"]
# red 7 "(^x.xx)(^x.xx)";;
- : term list =
[term "(^x.xx)^x.xx"; term "(^x.xx)^x.xx"; term "(^x.xx)^x.xx";
term "(^x.xx)^x.xx"; term "(^x.xx)^x.xx"; term "(^x.xx)^x.xx";
term "(^x.xx)^x.xx"; term "..."]
#

手計算に自信が無い時に使うといいかなー。

[Coq] Coq Advent Calender : unfold (10 of 25)

 Coq でよく使われる tactic の10番目は unfold です。unfold は関数定義を展開します。
 unfold だけで使う場合も有りますが、fold と組み合わせて使う事もあります。fold は展開された関数を元に戻します。unfold して、simpl とか rewrite とかして、また fold して戻すと式が良い具合に変形されている場合があります。ここではそういう例を見てみましょう。

 まず、sum n := 1 + 2 + ... + nという関数を定義します。

Coq < Fixpoint sum n :=
Coq < match n with
Coq < | O => O
Coq < | S n' => S n' + sum n'
Coq < end.
sum is recursively defined (decreasing on 1st argument)

 次に、sum n = 1/2 * n * (n + 1) であることを証明したいのですが、割り算が入ると面倒ですから、次の定理を証明する事にします。今回は環に関する自動証明器の ring を使いたいのでArithとRingをImportしておきます。
 nに関する帰納法で、n=0の場合はさらっと証明を流すことにします。

Coq < Require Import Arith Ring.

Coq < Lemma Sample_of_unfold : forall n, 2 * sum n = n * (n + 1).
1 subgoal

============================
forall n : nat, 2 * sum n = n * (n + 1)

Sample_of_unfold < induction n.
2 subgoals

============================
2 * sum 0 = 0 * (0 + 1)

subgoal 2 is:
2 * sum (S n) = S n * (S n + 1)

Sample_of_unfold < reflexivity.
1 subgoal

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 * sum (S n) = S n * (S n + 1)

 ここで、sum を unfold して、fold すると式が少し変形されます。

Sample_of_unfold < unfold sum.
1 subgoal

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 *
(S n +
(fix sum (n0 : nat) : nat :=
match n0 with
| 0 => 0
| S n' => S n' + sum n'
end) n) = S n * (S n + 1)

Sample_of_unfold < fold sum.
1 subgoal

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 * (S n + sum n) = S n * (S n + 1)

 ここでreplaceを使ってちょっと左辺を書き換えます。書き換えて良い証明はsubgoal 2と後回しです。


Sample_of_unfold < replace (2 * (S n + sum n)) with (2 * S n + 2 * sum n).
2 subgoals

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 * S n + 2 * sum n = S n * (S n + 1)

subgoal 2 is:
2 * S n + 2 * sum n = 2 * (S n + sum n)

 ここでIHnを用いて書き換えて式変形を ring で自動証明します。後回しにしたものもringで一発です。


Sample_of_unfold < rewrite IHn.
2 subgoals

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 * S n + n * (n + 1) = S n * (S n + 1)

subgoal 2 is:
2 * S n + 2 * sum n = 2 * (S n + sum n)

Sample_of_unfold < ring.
1 subgoal

n : nat
IHn : 2 * sum n = n * (n + 1)
============================
2 * S n + 2 * sum n = 2 * (S n + sum n)

Sample_of_unfold < ring.
Proof completed.

Sample_of_unfold < Qed.

09 December, 2010

[Coq] Coq Advent Calender : simpl (9 of 25)

 Coq でよく使われる tactic の9つ目は simpl です。simpl はβι簡約(beta, iota)をして式を簡単にします。βι簡約が何かについては「言語ゲーム」の記事が判りやすいです。β簡約は関数適用で、ι簡約だと再帰的に関数が適用される、と考えると良いです。

 予め、足し算の定義を示しておきます。plus を含んだ式を simpl すると、一つ目の変数 n について再帰的にパターンマッチして式を変形してくれます。

Coq < Print plus.
plus =
fix plus (n m : nat) : nat := match n with
| 0 => m
| S p => S (plus p m)
end
: nat -> nat -> nat

Argument scopes are [nat_scope nat_scope]

 では足し算を使った simpl の使用例です。

Coq < Lemma Sample_of_simpl : forall n m, n + m = m + n.
1 subgoal

============================
forall n m : nat, n + m = m + n

Sample_of_simpl < intros n m.
1 subgoal

n : nat
m : nat
============================
n + m = m + n

Sample_of_simpl < induction n.
2 subgoals

m : nat
============================
0 + m = m + 0

subgoal 2 is:
S n + m = m + S n

Sample_of_simpl < simpl.
2 subgoals

m : nat
============================
m = m + 0

subgoal 2 is:
S n + m = m + S n

simpl を使うと左辺の0 + mがnに簡約されますが、右辺は変化がありません。ここは別途証明しておいた(というか標準ライブラリにはいっている)定理のplus_n_Oを使って書き換える事にします。

Sample_of_simpl < Check plus_n_O.
plus_n_O
: forall n : nat, n = n + 0

Sample_of_simpl < erewrite <- plus_n_O.
2 subgoals

m : nat
============================
m = m

subgoal 2 is:
S n + m = m + S n

Sample_of_simpl < reflexivity.
1 subgoal

n : nat
m : nat
IHn : n + m = m + n
============================
S n + m = m + S n

帰納法の後半も simpl を使い、別の補題plus_n_Smを使います。

Sample_of_simpl < simpl.
1 subgoal

n : nat
m : nat
IHn : n + m = m + n
============================
S (n + m) = m + S n

Sample_of_simpl < Check plus_n_Sm.
plus_n_Sm
: forall n m : nat, S (n + m) = n + S m

Sample_of_simpl < erewrite <- plus_n_Sm.
1 subgoal

n : nat
m : nat
IHn : n + m = m + n
============================
S (n + m) = S (m + n)

Sample_of_simpl < erewrite IHn.
1 subgoal

n : nat
m : nat
IHn : n + m = m + n
============================
S (m + n) = S (m + n)

Sample_of_simpl < reflexivity.
Proof completed.

Sample_of_simpl < Qed.

08 December, 2010

[Coq] Coq Advent Calender : intro (8 of 25)

 Coq でよく使われる tactic の8つ目は intro です。基本的には前に説明したintrosと同じで、introsと違って一度に1つしかintro出来ないのが違います。
 あと、introsと違ってintroの場合は、~Pの形のゴールを、Pという仮定と、Falseというゴールに変形出来ます。~PはP->Falseなのですが、intros.では~Pは~Pのままです。

 intro.を使った例です。

Coq < Lemma Sample_of_intro : forall P, ~~~P -> ~P.
1 subgoal

============================
forall P : Prop, ~ ~ ~ P -> ~ P

Sample_of_intro < intro P.
1 subgoal

P : Prop
============================
~ ~ ~ P -> ~ P

Sample_of_intro < intro nnnp.
1 subgoal

P : Prop
nnnp : ~ ~ ~ P
============================
~ P

Sample_of_intro < intro p.
1 subgoal

P : Prop
nnnp : ~ ~ ~ P
p : P
============================
False

Sample_of_intro < elim nnnp.
1 subgoal

P : Prop
nnnp : ~ ~ ~ P
p : P
============================
~ ~ P

Sample_of_intro < intro np.
1 subgoal

P : Prop
nnnp : ~ ~ ~ P
p : P
np : ~ P
============================
False

Sample_of_intro < elim np.
1 subgoal

P : Prop
nnnp : ~ ~ ~ P
p : P
np : ~ P
============================
P

Sample_of_intro < exact p.
Proof completed.

Sample_of_intro < Qed.

07 December, 2010

[Coq] Coq Advent Calender : assert (7 of 25)

 Coq でよく使われる tactic の7つ目は assert です。

 1つ目のapplyのところで書いた様に、Coqの証明は最初にゴールがあって、それを仮定と逆に戻して行く格好になっています。しかし証明する人間は仮定からゴールを考える方が楽な場合、つまり証明途中で適当な補題を作りたくなる場合があります。
 補題はある程度汎用性があるならば、予め切り出して証明しておいた方が良いと思いますが、使い捨て的な補題は assert を使うと、証明内証明みたいに作る事ができます。例えば下記の例で、

Coq < Lemma Sample_of_assert : forall P Q, (P /\ P) -> (Q /\ Q) -> (P /\ Q).
1 subgoal

============================
forall P Q : Prop, P /\ P -> Q /\ Q -> P /\ Q

Sample_of_assert < intros P Q pp qq.
1 subgoal

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
============================
P /\ Q


 ここまで証明した段階で、forall X, X /\ X -> Xという補題があれば、ppからPを、qqからQを取り出せて便利そうだと考えました。そこでassertを使います。すると現在の証明課題より先に、まずこの補題が証明課題になります。

Sample_of_assert < assert(H: forall X, X /\ X -> X).
2 subgoals

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
============================
forall X : Prop, X /\ X -> X

subgoal 2 is:
P /\ Q

Sample_of_assert < intros X xx.
2 subgoals

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
X : Prop
xx : X /\ X
============================
X

subgoal 2 is:
P /\ Q

Sample_of_assert < destruct xx as [x _].
2 subgoals

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
X : Prop
x : X
============================
X

subgoal 2 is:
P /\ Q

Sample_of_assert < exact x.
1 subgoal

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
H : forall X : Prop, X /\ X -> X
============================
P /\ Q

 assertで設定した補題 H の証明が終わると、以後は仮定 H としてそれを使う事が出来ます。

Sample_of_assert < split.
2 subgoals

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
H : forall X : Prop, X /\ X -> X
============================
P

subgoal 2 is:
Q

Sample_of_assert < apply (H P).
2 subgoals

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
H : forall X : Prop, X /\ X -> X
============================
P /\ P

subgoal 2 is:
Q

Sample_of_assert < exact pp.
1 subgoal

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
H : forall X : Prop, X /\ X -> X
============================
Q

Sample_of_assert < apply (H Q).
1 subgoal

P : Prop
Q : Prop
pp : P /\ P
qq : Q /\ Q
H : forall X : Prop, X /\ X -> X
============================
Q /\ Q

Sample_of_assert < exact qq.
Proof completed.

Sample_of_assert < Qed.

06 December, 2010

[Coq] Coq Advent Calender : omega (6 of 25)

 Coq でよく使われる tactic の6つ目は omega です...ってanarchy proofは課題が数学寄りに偏っているから、順序が6つ目というのはあまり気にしないで下さい。

 omega タクティックはforallとかexistsとかを含まない形のPresburger算術 (Wikipedia)の式を自動証明してくれます。端的に言うと、

  • 割り算無し

  • 変数と変数の掛け算が入っているものはNG、変数と定数(2, 3などの具体的な整数)の掛け算はOK

みたいな感じの等式 or 不等式を証明します。
 Coqがよく使われる理由は、この手の数式関係の自動証明が便利に使えるからだと思います。整数の等式不等式に関する公理定理を組み合わせて証明するとか大変ですからね。

簡単な例はこんなのです。使い方は簡単で、Require Import Omega して、introsとかして、omega と入力するだけ。

Coq < Require Import Omega.

Coq < Lemma Sample_of_omega : forall x:nat, x > 1 -> 3 * x > x + 2.
1 subgoal

============================
forall x : nat, x > 1 -> 3 * x > x + 2

Sample_of_omega < intros.
1 subgoal

x : nat
H : x > 1
============================
3 * x > x + 2

Sample_of_omega < omega.
Proof completed.


 内部で実行された自動証明を見てみると、こんな感じらしい。

Sample_of_omega < Undo.
1 subgoal

x : nat
H : x > 1
============================
3 * x > x + 2

Sample_of_omega < info omega.
== refine (Decidable.dec_not_not (3 * x > x + 2) (dec_gt (3 * x) (x + 2)) _);
intro H0;
refine ((_:3 * x <= x + 2 -> False) (not_gt (3 * x) (x + 2) H0));
clear H0; intro H0;
refine ((_:(Z_of_nat x > Z_of_nat 1)%Z -> False) (inj_gt x 1 H));
change ((Z_of_nat x > 1)%Z -> False); clear H;
intro H;
refine ((_:(Z_of_nat (3 * x) <= Z_of_nat (x + 2))%Z -> False)
(inj_le (3 * x) (x + 2) H0));
refine ((fun (P : Z -> Prop) (H : P (Z_of_nat 3 * Z_of_nat x)%Z) =>
eq_ind_r P H (inj_mult 3 x))
(fun x0 : Z => (x0 <= Z_of_nat (x + 2))%Z -> False) _);
change ((3 * Z_of_nat x <= Z_of_nat (x + 2))%Z -> False);
refine ((fun (P : Z -> Prop) (H : P (Z_of_nat x + Z_of_nat 2)%Z) =>
eq_ind_r P H (inj_plus x 2))
(fun x0 : Z => (3 * Z_of_nat x <= x0)%Z -> False) _);
change ((3 * Z_of_nat x <= Z_of_nat x + 2)%Z -> False);
clear H0; intro H0; refine (ex_ind _ (intro_Z x));
intro Zvar2; intro Omega11; refine (and_ind _ Omega11);
clear Omega11; intro Omega8; intro Omega11;
refine ((_:(0 <= Z_of_nat x + -1 + - (1))%Z -> False)
(Zgt_left (Z_of_nat x) 1 H)); clear H;
refine ((fun (P : Z -> Prop) (H : P Zvar2) => eq_ind_r P H Omega8)
(fun x : Z => (0 <= x + -1 + - (1))%Z -> False) _);
change ((0 <= Zvar2 + -1 + -1)%Z -> False);
refine (fast_Zplus_assoc_reverse Zvar2 (-1)
(-1) (fun x : Z => (0 <= x)%Z -> False) _);
change ((0 <= Zvar2 + -2)%Z -> False);
refine (fast_Zred_factor0 Zvar2
(fun x : Z => (0 <= x + -2)%Z -> False) _);
intro Omega10;
refine ((_:(0 <= Z_of_nat x + 2 + - (3 * Z_of_nat x))%Z -> False)
(Zle_left (3 * Z_of_nat x) (Z_of_nat x + 2) H0));
clear H0;
refine ((fun (P : Z -> Prop) (H : P Zvar2) => eq_ind_r P H Omega8)
(fun x0 : Z => (0 <= x0 + 2 + - (3 * Z_of_nat x))%Z -> False)
_);
refine ((fun (P : Z -> Prop) (H : P Zvar2) => eq_ind_r P H Omega8)
(fun x : Z => (0 <= Zvar2 + 2 + - (3 * x))%Z -> False) _);
refine (fast_Zmult_comm 3 Zvar2
(fun x : Z => (0 <= Zvar2 + 2 + - x)%Z -> False) _);
refine (fast_Zopp_mult_distr_r Zvar2 3
(fun x : Z => (0 <= Zvar2 + 2 + x)%Z -> False) _);
change ((0 <= Zvar2 + 2 + Zvar2 * -3)%Z -> False);
refine (fast_Zplus_comm (Zvar2 + 2) (Zvar2 * -3)
(fun x : Z => (0 <= x)%Z -> False) _);
refine (fast_Zplus_assoc (Zvar2 * -3) Zvar2 2
(fun x : Z => (0 <= x)%Z -> False) _);
refine (fast_Zred_factor3 Zvar2 (-3)
(fun x : Z => (0 <= x + 2)%Z -> False) _);
change ((0 <= Zvar2 * Zneg (3 - 1) + 2)%Z -> False);
intro Omega9;
cut ((Zvar2 * -2 + 2)%Z = ((Zvar2 * -1 + 1) * 2 + 0)%Z);[intro H|idtac].
refine ((_:(Zvar2 * -2 + 2)%Z = ((Zvar2 * -1 + 1) * 2 + 0)%Z -> False) H);
clear H; intro auxiliary;
refine ((_:(0 <= (Zvar2 * -1 + 1) * 2 + 0)%Z -> False)
(OMEGA1 (Zvar2 * -2 + 2) ((Zvar2 * -1 + 1) * 2 + 0)
auxiliary Omega9)); clear auxiliary Omega9;
intro Omega9;
cut (2 > 0)%Z;[intro H|idtac].
refine ((_:(2 > 0)%Z -> False) H); clear H;
cut (2 > 0)%Z;[intro H|idtac].
refine ((_:(2 > 0)%Z -> (2 > 0)%Z -> False) H);
clear H; intro auxiliary_1; intro auxiliary_2;
refine ((_:(0 <= Zvar2 * -1 + 1)%Z -> False)
(Zmult_le_approx 2 (Zvar2 * -1 + 1) 0 auxiliary_1
auxiliary_2 Omega9));
clear auxiliary_1 auxiliary_2 Omega9; intro Omega9;
refine ((_:(0 <= Zvar2 * 1 + -2 + (Zvar2 * -1 + 1))%Z -> False)
(OMEGA2 (Zvar2 * 1 + -2) (Zvar2 * -1 + 1) Omega10 Omega9));
refine (fast_OMEGA13 Zvar2 (-2) 1 1 (fun x : Z => (0 <= x)%Z -> False)
_); change ((0 <= Zneg (2 - 1))%Z -> False);
change ((0 ?= Zneg (2 - 1))%Z <> Gt -> False);
change (Gt <> Gt -> False); intro H; refine (False_ind False _);
cut (Gt = Gt);[intro H0|idtac].
refine ((_:Gt = Gt -> False) H0); clear H0;
cut (Gt <> Gt);[intro H0|idtac].
refine ((_:Gt <> Gt -> Gt = Gt -> False) H0); clear H0;
intro H0; intro H1; exact (H0 H1).

exact H.

change (Gt = Gt); exact (refl_equal Gt).

change ((2 ?= 0)%Z = Gt); change (Gt = Gt); change (Gt = Gt);
exact (refl_equal Gt).

change ((2 ?= 0)%Z = Gt); change (Gt = Gt); change (Gt = Gt);
exact (refl_equal Gt).

refine (fast_OMEGA11 Zvar2 (-1) 1 0 2
(fun x : Z => (Zvar2 * -2 + 2)%Z = x) _);
change ((Zvar2 * -2 + 2)%Z = (Zvar2 * -2 + 2)%Z);
change ((Zvar2 * -2 + 2)%Z = (Zvar2 * -2 + 2)%Z);
exact (refl_equal (Zvar2 * -2 + 2)%Z).

Proof completed.

Sample_of_omega <

05 December, 2010

[Coq] Coq Advent Calender : intros (5 of 25)

 Coq でよく使われる tactic の5つ目は intros です。

 実は過去4回の記事でも毎回 intros を使用していました。forall とかで定義された変数や -> の左の式とかを仮定に持って行く為に使います。introの複数形なので、introを必要な回数繰り返してもOKです。
 intros. だけでも勝手に適当に名前(仮定は通常 H? みたいな名前)を付けてくれますが、判りやすさを考えると自分で適切な名前を付けた方が良いと思います。

Coq < Lemma Sample_of_intros : forall A B C:Prop, (A->B->C) -> (A->B) -> A -> C.
1 subgoal

============================
forall A B C : Prop, (A -> B -> C) -> (A -> B) -> A -> C

Sample_of_intros < intros A B C abc ab a.
1 subgoal

A : Prop
B : Prop
C : Prop
abc : A -> B -> C
ab : A -> B
a : A
============================
C

Sample_of_intros < apply abc.
2 subgoals

A : Prop
B : Prop
C : Prop
abc : A -> B -> C
ab : A -> B
a : A
============================
A

subgoal 2 is:
B

Sample_of_intros < exact a.
1 subgoal

A : Prop
B : Prop
C : Prop
abc : A -> B -> C
ab : A -> B
a : A
============================
B

Sample_of_intros < apply ab; exact a.
Proof completed.

Sample_of_intros < Qed.
intros A B C abc ab a.
apply abc.
exact a.

apply ab; exact a.

Sample_of_intros is defined

Coq <

04 December, 2010

[Coq] Coq Advent Calender : destruct (4 of 25)

 Coq でよく使われる tactic の4つ目は destruct です。

 destruct は基本的にはある項を場合分けする時使うのですが、仮定の中にある /\ とか \/ とか exists とかを分解するのに使うのに便利です。(それ以外のときは induction とか case_eq とか使う事が多い様にも思います。)
 下記の例では仮定 abc を分解して仮定 a, b, c を作っています。 


Coq < Lemma Sample_of_destruct : forall A B C:Prop,
Coq < A /\ (B /\ C) -> (A /\ B) /\ C.
1 subgoal

============================
forall A B C : Prop, A /\ B /\ C -> (A /\ B) /\ C

Sample_of_destruct < intros A B C abc.
1 subgoal

A : Prop
B : Prop
C : Prop
abc : A /\ B /\ C
============================
(A /\ B) /\ C

Sample_of_destruct < destruct abc as [a [b c]].
1 subgoal

A : Prop
B : Prop
C : Prop
a : A
b : B
c : C
============================
(A /\ B) /\ C

Sample_of_destruct < split; [split|]; assumption.
Proof completed.

Sample_of_destruct < Qed.
intros A B C abc.
destruct abc as (a, (b, c)).
split; [ split | idtac ]; assumption.

Sample_of_destruct is defined

Coq <

[Coq] Coq Advent Calender : rewrite (3 of 25)

 Coq でよく使われる tactic の3つ目は rewrite です。

 rewrite は仮定にある等式を使ってゴールを書き換えます。下記の例では、リスト xs について帰納法を用いて証明していますが、ゴールの中に含まれるlength (xs ++ ys)を、帰納法の仮定IHxsを用いてrewriteすることでlength xs + length ysに書き換えています。


Coq < Lemma Sample_of_rewrite : forall (A:Set)(xs ys:list A),
Coq < length (xs ++ ys) = length xs + length ys.
1 subgoal

============================
forall (A : Set) (xs ys : list A),
length (xs ++ ys) = length xs + length ys

Sample_of_rewrite < intros A xs ys.
1 subgoal

A : Set
xs : list A
ys : list A
============================
length (xs ++ ys) = length xs + length ys

Sample_of_rewrite < induction xs.
2 subgoals

A : Set
ys : list A
============================
length (nil ++ ys) = length nil + length ys

subgoal 2 is:
length ((a :: xs) ++ ys) = length (a :: xs) + length ys

Sample_of_rewrite < reflexivity.
1 subgoal

A : Set
a : A
xs : list A
ys : list A
IHxs : length (xs ++ ys) = length xs + length ys
============================
length ((a :: xs) ++ ys) = length (a :: xs) + length ys

Sample_of_rewrite < simpl.
1 subgoal

A : Set
a : A
xs : list A
ys : list A
IHxs : length (xs ++ ys) = length xs + length ys
============================
S (length (xs ++ ys)) = S (length xs + length ys)

Sample_of_rewrite < rewrite IHxs.
1 subgoal

A : Set
a : A
xs : list A
ys : list A
IHxs : length (xs ++ ys) = length xs + length ys
============================
S (length xs + length ys) = S (length xs + length ys)

Sample_of_rewrite < reflexivity.
Proof completed.

Sample_of_rewrite < Qed.
intros A xs ys.
induction xs.
reflexivity.

simpl in |- *.
rewrite IHxs in |- *.
reflexivity.

Sample_of_rewrite is defined

Coq <

[Coq] Coq Advent Calender : auto (2 of 25)

 Coq でよく使われる tactic の2つ目は auto です。

 Coq は簡単な証明は自動的に証明してくれる機能が幾つかありますが、auto はよく使われます。autoが何をやっているか知りたい場合は、autoの代わりにinfo autoと入力すると、autoの中で何をしてるかが判ります。Coqの初学者にはinfoは便利ですよ。


Coq < Lemma Sample_of_auto : forall A B:Prop, ((((A->B)->A)->A)->B)->B.
1 subgoal

============================
forall A B : Prop, ((((A -> B) -> A) -> A) -> B) -> B

Sample_of_auto < auto.
Proof completed.

Sample_of_auto < Qed.
auto.

Sample_of_auto is defined

Coq <

[Coq] Coq Advent Calender : apply (1 of 25)

 技術的 Advent Calender という概念を知らなかったのですが、面白そうなので遅ればせながら始めてみました。という訳で12/1から開始出来なかったのはご容赦下さい。
 先日のCoq Party絡みで集計されたCoqでよく使われるtacticのリストの順に、そのtacticが使われている証明例を書いてみます。

 最初は apply です。
 「この仮定(下記の例では pq)を使うとゴール(下記の例では Q)が出て来る」という時に、apply pq とすると、ゴール Q が変化します。学校で習う普通の証明の順序と違って、Coqの証明はゴールを一歩一歩、仮定に戻して行きます。その時使うのが apply です。


Coq < Lemma Sample_of_apply : forall P Q:Prop, P -> (P->Q) -> Q.
1 subgoal

============================
forall P Q : Prop, P -> (P -> Q) -> Q

Sample_of_apply < intros P Q p pq.
1 subgoal

P : Prop
Q : Prop
p : P
pq : P -> Q
============================
Q

Sample_of_apply < apply pq.
1 subgoal

P : Prop
Q : Prop
p : P
pq : P -> Q
============================
P

Sample_of_apply < exact p.
Proof completed.

Sample_of_apply < Qed.
intros P Q p pq.
apply pq.
exact p.

Sample_of_apply is defined

Coq <

01 November, 2010

[TAPL][Coq] Chapter 3: Boolean Expression

TAPLのChapter 3のBoolean式の部分に付いてCoqで証明してみました。
コードは http://ideone.com/COzSO に公開しています。

12 October, 2010

[TAPL] Types and Programming Languages Reading at Tokyo

Types and Programming Languages (通称 TAPL) の読書会の第0回が先日あったので報告。

TAPL読書会ですが、下記の様にGoogle groupが出来ました。開催情報とか流れるので興味のある方は登録をお勧めします。
http://groups.google.co.jp/group/taplreading-tokyo
次回開催は 11/3午後で、会場は同じく豆蔵オフィスです。

前回は、Chap.1はスキップ、Chap.2から読み始め、Chap.3の途中まででした。
次回はChap.3の続きから初めて、Chap.4(というか実装のChapterはこの先も)はスキップ、Chap.5を読む予定、となっています。
参加者は予習復習前提で、次回の範囲は予め読んで来ることが前提と、硬派な勉強会ということになりました。
Chap.4のような実装の箇所は各自実装したい人が自分で実装してちょっとデモをするということで。

参加者ですが、前回は総勢4名もCS専攻の大学院生の方が来てくれたのは良かったです。素人の社会人だけで勉強会してると簡単に行き詰るので。

懇親会も大変盛り上がっていたようで、大変有意義な一日でした。しかし、積読率高いですね>TAPL。買ったけど読んでなかったとか、Chap.3までは読んだけどとか、そういう話ばっかり。

26 September, 2010

[event] Reading TAPL

Types and Programming Languages ( http://www.amazon.co.jp/dp/0262162091 ) という定番の教科書を読もうという読書会です。
東京近辺の方はぜひどうぞ。

http://atnd.org/events/8291

20 September, 2010

[Scala] Fibonacci series including 7110

 今更ながら http://recruit.drecom.co.jp/event2010 の「7110を含むフィボナッチ数列で、初期値の組み合わせが一番小さいものをあげろ。※自然数に限る」をScalaで解いてみた。
 もうイベントは終わったから解答公開してもいいよね。

 fib が 7110以下の範囲のフォボナッチ数列を計算する関数。zipとか使って格好良く書けるかもとも思うが、判りやすさ優先で。ysのところにxs'と書けないScalaは残念な感じだ。


scala> def fib(n0:Int,n1:Int) = {
| def f(xs:List[Int]):List[Int] = xs match {
| case a::b::ys if a < 7110 => f((a+b)::xs)
| case _ => xs
| }
| f(n1::n0::Nil)
| }
fib: (Int,Int)List[Int]

scala> def solve = for( n1 <- (1 to 100).toList;
| n0 <- (1 to n1).toList if fib(n0,n1).head==7110
| ) yield {(n0,n1)}
solve: List[(Int, Int)]

scala> solve
res7: List[(Int, Int)] = List((16,70), (70,86))
scala>

24 July, 2010

[Coq] Coq-99 : Part 1

 前にS-99: Ninety-Nine Scala Problemsというのを紹介しましたが、Coqでも入門者が勉強用に解く為の問題集というのを考えてみました。

 まずは命題論理の証明の課題。勿論、tautoとかで一発だと思いますが、練習問題なので入門者は自力で解こう。
 Admitted.を消して証明を書く事が期待されています。

Section Prop_Logic.
Lemma Coq_01 : forall A B C:Prop, (A->B->C) -> (A->B) -> A -> C.
Admitted.
Lemma Coq_02 : forall A B C:Prop, A /\ (B /\ C) -> (A /\ B) /\ C.
Admitted.
Lemma Coq_03 : forall A B C D:Prop, (A -> C) /\ (B -> D) /\ A /\ B -> C /\ D.
Admitted.
Lemma Coq_04 : forall A : Prop, ~(A /\ ~A).
Admitted.
Lemma Coq_05 : forall A B C:Prop, A \/ (B \/ C) -> (A \/ B) \/ C.
Admitted.
Lemma Coq_06 : forall A, ~~~A -> ~A.
Admitted.
Lemma Coq_07 : forall A B:Prop, (A->B)->~B->~A.
Admitted.
Lemma Coq_08: forall A B:Prop, ((((A->B)->A)->A)->B)->B.
Admitted.
Lemma Coq_09 : forall A:Prop, ~~(A\/~A).
Admitted.
End Prop_Logic.


解答例は例えばこれとか。

[Joke]Falso : HyperVerifier and HyperProver

 twitterで知ったジョークサイトなんだが、Falso, by Estatis Inc.があります。

 「ある公理を導入すれば」なんでも証明出来てしまう(笑)という話で、それをいかにも画期的新製品みたいな感じに仕上げているのがおかしい。

 追記:togetter: Falso 定理証明系の大革命

19 July, 2010

[FM] Maude

 今日はFormal Methods Forumの勉強会で、いつものCoq (CPDT)の話の他に、Maudeをちょっと触ってみた、という話をしました。

 Maudeは項書き換え系の言語で形式仕様記述やモデル検査、DSL記述などにも使える言語です。
 Coqと違って証明も出来るけどメインは仕様記述&それ自体がプロトタイプとして動く、かなぁ。パズルとかにも役に立つかも。

 あわてて作った資料なんで完成度は低いですがこちら。
Slideshare上のプレゼン
PDF資料

 Maudeに興味のある方はコメント頂けると嬉しいです。

03 July, 2010

[Coq] CoqUn -- Summer Coq Event in Nagoya

 2010/08/29に名古屋でCoq庵 -- 日本の夏、証明の夏というCoqのイベントが開催されます。
 といっても、Coqユーザが集まって話をしようという比較的緩いイベントなんで、Coq初めてという人も参加すると良いのではなかろうか。入門的な話もあります。

[Coq] Puzzle from Hiyama-san's Blog

 檜山さんのblogに掛け算から足し算を作る(パズルとしてやってみよう)という問題が載っています。
 Coqの練習問題としてちょうど良いかと思い解いてみました。
 思ったより時間がかかってしまい、解き終わる前にネタばらしが出ちゃったのだけど、一応、解いた解答です。http://ideone.com/Staa3

 分配則は簡単だったが、交換則を思いつくまでに苦労した。交換測を思いついたらその延長で結合則もなんとか。ただ、証明はちまちまとrewriteしてるだけ。ここは本当は自動化できるんだろうなぁ。
 Coq初心者的には、inv関係の{a'|a*a'=1}みたいな記法の使い方に慣れたのが良かったか。あとAxiom eq_dec_0:forall a:G, {a=0}+{a<>0}みたいなdecide出来る公理の便利さを実感した。

14 June, 2010

[Scala] Scala 2.8 Continuation (1)

root/compiler-plugins/continuations/trunk/doc/examples/continuationsに載っているサンプルコードをREPLで写経してみます。

Test0.scala

scala> import scala.util.continuations._
import scala.util.continuations._

scala> reset {
| 2 * {
| println("up")
| val x = shift((k:Int=>Int) => k(k(k(8))))
| println("down")
| x
| }
| }
up
down
down
down
res1: Int = 64

これはdef k(i:Int):Int = {println("down"); 2*i}と考えればOK。

Test1.scala

scala> object Test1 {
| def testThisMethod() = {
| 1 + (shift((k:Int=>Int) => k(k(k(17)))))
| }
| def testThisCallingMethod() = {
| testThisMethod() * 2
| }
| def m() {
| val result = reset(testThisCallingMethod())
| println(result)
| }
| }
defined module Test1

// ((((((17 + 1) * 2) + 1) * 2) + 1) * 2) = 150

scala> Test1.m()
150

k = (_+1)*2 と考えればこれも予想通りの結果。
resetの中にshiftを書くのはOKだが、resetなしでshiftだけの関数を定義しようとすると下記の様にエラーになります。仕方が無いのでobject Test1 {...} に包みます。

scala> def testThisMethod() = {
| 1 + (shift((k:Int=>Int) => k(k(k(17)))))
| }
:2: error: type mismatch;
found : Int @scala.util.continuations.cpsSynth @scala.util.continuations.cpsParam[Int,Int]
required: Int
object RequestResult$line4$object {
^


Test2.scala

scala> object Test2 {
| def methA() = {
| def fun(k:Int=>String) = {
| for(i <- List(1,2,3,4)) println(k(i))
| Some("returnvalue")
| }
| 2 * shift(fun)
| }
| def methB() = {
| "+++ "+methA().toString()
| }
| def m() = reset(methB())
| }
defined module Test2

scala> Test2.m()
+++ 2
+++ 4
+++ 6
+++ 8
res0: Some[java.lang.String] = Some(returnvalue)

k = "+++" + (2 * _).toString() ですか。

13 June, 2010

[Scala] Scala 2.8 RC4 and continuation plugin

 Scala 2.8 RC4 が出ました --- が、なんかバグがあるらしく来週には RC5 が出るそうです。なんか品質安定しませんね>Scala 2.8。

 RC4からは限定継続のコンパイラプラグインが付属される様になりました。
 使い方はこんな感じ。Scala 2.8 RC4を導入したディレクトリに居るとします。pluginにパスを通し、continuationをenableにする必要があります。

% cd ~/scala28rc4
% ./bin/scala -Xpluginsdir ./misc/scala-devel/plugins/ ¥
-Xplugin:continuations -P:continuations:enable
Welcome to Scala version 2.8.0.RC4 (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_20).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import scala.util.continuations._
import scala.util.continuations._

scala> reset {
| shift { (k:Int=>Int) =>
| k(k(k(7)))
| } + 1
| }
res0: Int = 10

scala>


限定継続をおいおい勉強しようと思います。

20 May, 2010

[Coq] How to define complex inductions

Coq の証明の書き方パターンを yoshihiro503 さんに教わったので、忘れないうちに。

自然数に関する定理を証明したい場合に、下記の様な補題が欲しかったとします。
Lemma nat2_ind : forall P:nat -> Prop, P 0 -> P 1 ->
(forall n:nat, P n -> P (S n) -> P (S (S n))) ->
(forall n:nat, P n).

教わったコードを元に自分なりに書いてみたのが下記の証明です。
  
============================
forall P : nat -> Prop,
P 0 ->
P 1 ->
(forall n : nat, P n -> P (S n) -> P (S (S n))) -> forall n : nat, P n

nat2_ind < intros P H0 H1 IH.
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
============================
forall n : nat, P n

nat2_ind < refine (fix ll n : P n := _). (* ここで refine を使って ll を定義するのがポイント *)
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
============================
P n

(* しかしここで apply ll しても駄目。llはIHの仮定のところで使う *)

nat2_ind < induction n.
2 subgoals

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
============================
P 0

subgoal 2 is:
P (S n)

nat2_ind < exact H0. (* n=0 のケースは P 0 *)
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
IHn : P n
============================
P (S n)

nat2_ind < induction n.
2 subgoals

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
IHn : P 0
============================
P 1

subgoal 2 is:
P (S (S n))

nat2_ind < exact H1. (* n=1 のケース *)
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
IHn : P (S n)
IHn0 : P n -> P (S n)
============================
P (S (S n))

nat2_ind < apply IH. (* IH を使う *)
2 subgoals

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
IHn : P (S n)
IHn0 : P n -> P (S n)
============================
P n

subgoal 2 is:
P (S n)

nat2_ind < apply ll. (* IHの仮定のところにはll を使ってOK *)
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
IHn : P (S n)
IHn0 : P n -> P (S n)
============================
P (S n)

nat2_ind < apply IHn0.
1 subgoal

P : nat -> Prop
H0 : P 0
H1 : P 1
IH : forall n : nat, P n -> P (S n) -> P (S (S n))
ll : forall n : nat, P n
n : nat
IHn : P (S n)
IHn0 : P n -> P (S n)
============================
P n

nat2_ind < apply ll. (* もう一度 ll を使う *)
Proof completed.

nat2_ind < Qed.

そもそもの証明のゴールをrefineを使ってllとして定義して、証明の中でllを使っているので非常に不思議なのですが、変な所(例えばrefine直後に)でapply llしてProof CompleteしてもQedしたとたんにエラーになります。エラーが出ない様にH0,H1,IHを使えば、証明が正しい事になります。

なお、同じ定理を短く書くとkikxさんの証明ゴルフみたいになるようです。すごい。

15 May, 2010

[Coq] Arithmetic in Coq : N

Nは二進数で表現された自然数の型です。

Require Import NArith.すると使用出来ます。型の定義は下記の様です。
Inductive N : Set :=  N0 : N | Npos : positive -> N


positiveは下記の様に定義されています。
Inductive positive : Set :=
xI : positive -> positive | xO : positive -> positive | xH : positive

こんな感じで正の数を表現しています。xHが最上位ビットで、xIが x -> 2x+1, xOが x -> 2x ということです。
Coq < Check (xO (xI (xO xH))).
10%positive : positive

これは(1~0~1~0)%positive.とも書けます。

natとの変換は下記で可能。
nat_of_N : N -> nat
N_of_nat : nat -> N


演算子は
Ndouble_plus_one, Ndouble : N -> N
Nsucc, Npred : N -> N
Nplus, Nminus, Nmult : N -> N -> N 。+, -, * もあり。
Ncompare : N -> N -> comparison 。Infixで ?=
comparisonは、
Inductive comparison : Set :=
Eq : comparison | Lt : comparison | Gt : comparison
Nlt, Ngt, Nle, Nge : N -> N -> Prop 。Infix で <, <=, >, >=
Nmin, Nmax : N -> N -> N
Ndiv2 : N -> N


Nに関する帰納法は次の2つの好きな方を使える。Nrectを用いてNindもある。

N_ind_double
: forall (a : N) (P : N -> Prop),
P 0%N ->
(forall a0 : N, P a0 -> P (Ndouble a0)) ->
(forall a0 : N, P a0 -> P (Ndouble_plus_one a0)) -> P a
Nrect
: forall P : N -> Type,
P 0%N -> (forall n : N, P n -> P (Nsucc n)) -> forall n : N, P n


Require Import ZArith.ZOdiv.すれば、
Ndiv_eucl : N -> N -> N * N
Ndiv : N -> N -> N
Nmod : N -> N -> N
Theorem Ndiv_eucl_correct: forall a b,
let (q,r) := Ndiv_eucl a b in a = (q * b + r)%N.

と除算も使える。

---
追記
ZArith.ZOdivと、ZArithの中でImportしてるZArith.Zdivとは互換性無いのだな。
ZArithを使わないことはまず無いので、ZOdivは使わないほうがよさそう。

14 May, 2010

[Coq] Arithmetic in Coq : nat

Coqで大きな自然数あるいは整数を使いたくなったので調べてみました。

★nat

 Coqで自然数というと基本はnatです。natというと自分で作る物であるかの様な気すらしますが勿論標準で備わっています。
 natを使う場合Require Import Arith.すると標準ライブラリが使えます。

●基本
Init.Peanoの中で、pred, plus, mult, le, lt, ge, gtが定義されていてImport不要で使える。

●大小比較関係

- eq_nat : nat -> nat -> Prop
-- heorem eq_nat_decide : forall n m, {eq_nat n m} + {~ eq_nat n m}. が使える
- lt_eq_lt_dec n m : {n < m} + {n = m} + {m < n}. こんな感じで使う
Definition nat_compare (n m:nat) :=
match lt_eq_lt_dec n m with
| inleft (left _) => Lt
| inleft (right _) => Eq
| inright _ => Gt
end.
-- le_lt_dec n m : {n <= m} + {m < n}. など


●四則演算

- 末尾再帰なtail_plusというのもある
- minus : nat -> nat -> nat. n - m とかも書ける。結果が負になる場合もOを返す。
- min, max : nat -> nat -> nat.
- div2, double : nat -> nat.
- 除算は無いが、Require Import Arith.Euclid. すれば商と剰余には下記がある
-- quotient : forall n, n > 0 ->
forall m:nat, {q : nat | exists r : nat, m = q * n + r /\ n > r}.
-- modulo : forall n, n > 0 ->
forall m:nat, {r : nat | exists q : nat, m = q * n + r /\ n > r}.

13 May, 2010

[Coq] FoCaLize Tutorial (2)

 [Coq] FoCaLize Tutorial (2)の続き。

 前回のextsubset.fclに
  theorem incl_remove_mem : all s : Self, all v : Val,
~(v << s) -> s <: s - v
proof = by property mem_congr, mem_remove, mem_incl ;
を追加すると自動証明が失敗するのでFoCaLizeの手動証明を試す予定なのだが...自動証明がそのまま通るみたいだ。

 が、ここは一応Tutorial通り、手動証明も試してみる事にする、証明を下記の様に直してみる。
    proof = <1>1 assume s : Self, v : Val, hypothesis Hv : ~(v << s),
prove s <: s - v
<2>1 assume w : Val, hypothesis Hw : w << s,
prove w << s - v
<3>1 prove ~(Val!( = )(w, v)) /\ w << s
<4>1 prove ~(Val!( = )(w, v))
by property mem_congr hypothesis Hv, Hw
<4>2 prove w << s
by hypothesis Hw
<4>f conclude
<3>f qed by property mem_remove step <3>1
<2>f qed by property mem_incl step <2>1
<1>f conclude ;

再度コンパイルするとこれでも通る。上記の様に手で証明を記述しても生成されたCoqの証明は短く分割されて読みやすくはなるもののやはり人間向けの証明とは言えない感じである。

証明に通らない例として
  theorem remove_insert : all s : Self, all v : Val,
v << s -> s = (s - v) + v
proof = <1>1 assume s : Self, v : Val, hypothesis Hv : v << s,
prove s = (s - v) + v
<2>1 assume w : Val, hypothesis Hw : w << s,
prove w << (s - v) + v
by property mem_insert, mem_remove
<2>2 assume w : Val, hypothesis Hw : w << (s - v) + v,
prove w << s
by property mem_insert, mem_remove, mem_congr, Val!diff_eq
<2>f qed by property eq_incl, mem_incl step <2>1, <2>2
<1>f conclude ;

を試すと下記の様にエラーが出る。
% ~/pkg/bin/focalizec extsubset.fcl
Invoking ocamlc...
>> /Users/miyamoto/pkg/focalize-0.6.0/bin/ocamlc -I /Users/miyamoto/pkg/lib/focalizec-0.6.0 -c extsubset.ml
Invoking zvtov...
>> /Users/miyamoto/pkg/focalize-0.6.0/bin/zvtov -zenon /Users/miyamoto/pkg/focalize-0.6.0/bin/zenon -new extsubset.zv
File "extsubset.fcl", line 61, characters 19-53:
Zenon error: could not find a proof within the memory size limit
### proof failed
%
これについてもチュートリアルに従い証明を完全に記述するとコンパイルに通る。

 なんとなくCoqに慣れてしまったので、FoCaLizeを用いて証明を記述する方が寧ろ判りにくいようにも思う。

10 May, 2010

[Coq] FoCaLize Tutorial (1)

 形式手法開発ツールのFoCaLizeをインストールして動かしてみました。

 FoCaLizeは言語名かつツール群の総称です。言語としては、ちょっとオブジェクト指向風(継承などの要素があったり、speciesというクラスっぽいものがある)な純粋関数型言語で、FoCaLizeという言語で書いたプログラムは、OCamlのコードが生成されると共に、(ちょっとしたヒントを人間が与える事で)Zenonという自動証明器がCoq用の証明を自動生成します。
 多分、普通のJavaプログラマとかには、素のCoqよりもFoCaLizeの方が習得しやすい思われる。逆にバリバリのOCamlerみたいな人には素のCoqを使う方が楽なんじゃなかろうか。
 開発元は例によってCoqなどと同じINRIAです。Coq界隈だけでもCoq自体に、FoCaLizeに、Ynotにとなんかツールが多過ぎて全然勉強がおいつきません。

★インストール

Downloadからtar ballをダウンロードして、
# ./configure
# make
# make install

するとOK。Coqが既に動くならそんなに大変じゃないと思う。(前提にghostscriptを要求されたり良く解らんところもあるが)

★superset.fcl

Tutorialに従い、まずはsuperset.fclをコンパイルしてみます。下記を superset.fcl として入力。文法は概ねOCaml風です。
use "basics" ;;
species Superset =
signature ( = ) : Self -> Self -> basics#bool ;
property eq_refl : all x : Self, x = x ;
property eq_symm : all x y : Self, x = y -> y = x ;
property eq_tran : all x y z : Self, x = y -> y = z -> x = z ;
theorem eq_symmtran : all x y z : Self, x = y -> x = z -> y = z
proof = by property eq_symm, eq_tran ;
end ;;
内容は大体見当がつくと思います。Selfというのは、OOな用語で言えば自クラスのことで、自インスタンス(Javaのthis)ではありません --- まぁ
Self -> Self -> basics#bool
を見ればそれは判るか。

% focalizec superset.fcl
でコンパイルすると、OCamlのコード (superset.ml) とかTheoremの証明 (superset.v) などが生成されます。eq_symm, eq_tran を使え、と指示するだけで自動証明されるのでCoqとかに不慣れでも使いやすいかも。ただ生成される証明が
exact(
(NNPP _ (fun zenon_G=>(zenon_notallex (fun x:abst_T=>(forall y:abst_T,(
forall z:abst_T,((Is_true (abst__equal_ x y))->((Is_true (abst__equal_
x z))->(Is_true (abst__equal_ y z))))))) (fun zenon_H2a=>(zenon_ex
abst_T (fun x:abst_T=>(~(forall y:abst_T,(forall z:abst_T,((Is_true (
...47行省略...
abst_eq_tran)))) zenon_H1b)) (zenon_notnot _ (refl_equal (abst__equal_
zenon_Tz_k zenon_Ty_e))) zenon_Hb)) zenon_H8 zenon_H9)) in (zenon_noteq
_ zenon_Ty_e zenon_H7)))) (fun zenon_H5=>(zenon_H6 zenon_H5)) zenon_H22)
) zenon_H23)) abst_eq_symm)) zenon_H24)) zenon_H25)) zenon_H26))
zenon_H27)) zenon_H28)) zenon_H29)) zenon_H2a)) zenon_G)))).

みたいな感じで、人間が読む証明じゃないよなー。あとNNPP : forall p : Prop, ~ ~ p -> pを使うのありなんだ、みたいな。

★subset.fcl

同様にsubset.fclを入力。
use "basics" ;;
open "superset" ;;

species Subset(Val is Superset) =
signature ( << ) : Val -> Self -> basics#bool ;

signature empty : Self ;
property mem_empty : all v : Val, ~(v << empty) ;

signature ( + ) : Self -> Val -> Self ;
property mem_insert : all v1 v2 : Val, all s : Self,
v1 << s + v2 <->
(Val!( = )(v1, v2) \/ v1 << s) ;

signature ( - ) : Self -> Val -> Self ;
property mem_remove : all v1 v2 : Val, all s : Self,
v1 << s - v2 <->
(~(Val!( = )(v1, v2)) /\ v1 << s) ;
end ;;

 内容は見れば判ると思いますが<<, +, -が集合の∈、要素の追加、削除に対応します。Val!( = )とうのはValクラスの( = )メソッドを呼び出す、みたいな意味です。

★extsubset.fcl

次に継承を使う例を。ExtSubsetはSubsetを継承します。
use "basics" ;;
open "superset" ;;
open "subset" ;;

species ExtSubset(Val is Superset) =
inherit Superset, Subset(Val) ;

signature ( <: ) : Self -> Self -> basics#bool ;
property mem_incl : all s1 s2 : Self,
s1 <: s2 <-> all v : Val, v << s1 -> v << s2 ;
theorem incl_refl : all s : Self, s <: s
proof = by property mem_incl ;
theorem incl_tran : all s1 s2 s3 : Self,
s1 <: s2 -> s2 <: s3 -> s1 <: s3
proof = by property mem_incl ;

theorem incl_empty : all s : Self, empty <: s
proof = by property mem_incl, mem_empty ;
theorem incl_insert : all s : Self, all v : Val, s <: s + v
proof = by property mem_insert, mem_incl ;
theorem incl_remove : all s : Self, all v : Val, s - v <: s
proof = by property mem_remove, mem_incl ;

let ( = ) (s1, s2) = if (s1 <: s2) then (s2 <: s1) else false ;

property mem_congr : all v1 v2 : Val, Val!( = ) (v1, v2) ->
(all s : Self, (v1 << s) <-> (v2 << s)) ;

theorem incl_insert_mem : all s : Self, all v : Val,
v << s -> s + v <: s
proof = by property mem_congr, mem_insert, mem_incl ;
end ;;


 この次には、自動証明が失敗するケースを扱います。

07 May, 2010

[Coq] ASN.1 Library is under construction

 この連休にCoqの練習がてら作っていたASN.1のライブラリで、全然完成してないのだがとりあえず出来たところまで公開。
 Coqdocで生成したドキュメントがTLV.htmlです。(コードが成長したら随時更新予定)

 ASN.1が何かについては、A Layman's Guide to a Subset of ASN.1, BER, and DERあたりを読んで下さい。電子証明書とかで使っているバイナリフォーマットなので、証明付きのライブラリを作りたいのだ。

 本当は
Inductive tlv_value : Set := 
| Primitive : string -> tlv_value
| Structured : list tlv -> tlv_value
.
みたいに定義したかったのだが、そうするとtlv_value, tlv に関する帰納法をうまく構成出来なかった。まだCPDT Chapter 3の読み込みが足りないのだろうなぁ。
 が、とりあえず幾つかの定理も証明できたし、出だしとしてはこんなものかもとも思う。

 Coqを勉強しようと思う様なプログラマの人は、ある程度複雑な型(相互再帰的だったり再帰がネストしていたり)を定義出来ると思うが、Coqは帰納型に対して帰納法を与える関数が作れないと駄目で、しかしそこが実は難しい、というのがこの連休に得た知見だ。

05 May, 2010

[Coq] How to use Record

 Coq で Record を使う方法を調べました。基本的には記述を簡単にする構文糖のようです。これを使うと、事前条件を満たす証明を要求するコンストラクタを簡単に書けます。
 使い方は下記の例を見てもらうのが簡単でしょう。

 まず正の有理数を表すRat型を作りたいと考えます。符号は無視するとして、分母が0で無いことを保証したい、分子分母が既約である事を保証したい、とします。こんな感じに書けます。

Record Rat : Set := mkRat {
numer : nat;
denom : nat;
denom_not_zero : denom <> O;
irreducible : forall g n d:nat,
(mult g n)=numer /\ (mult g d)=denom -> g = S O
}.

Ratで 1/2 を定義したい時はこんな感じになります。まず補題を証明しないと値を作れません。

Lemma two_not_zero : 2 <> 0.
Proof.
discriminate.
Qed.
Lemma one_two_irred : forall g n d:nat, (mult g n)=1 /\ (mult g d)=2 -> g = S O.
Admitted. (* 証明省略 *)

こうして初めて値を作れます。

Definition half : Rat := mkRat 1 2 two_not_zero one_two_irred.
Print half. (* mkRat 1 2 two_not_zero one_two_irred : Rat *)
Eval compute in (numer half). (* 1 : nat *)
Eval compute in (denom_not_zero half). (* two_not_zero *)

アクセサに見えるnumerなどは単なる関数です、従って同じ名前空間でnumerを他に定義出来ません。

Print numer.
numer =
fun r : Rat => let (numer, denom, _, _) := r in numer
: Rat -> nat

Ratに関する帰納法は下記の様になります。

Print Rat_ind.
(* forall P : Rat -> Prop,
(forall (numer denom : nat) (denom_not_zero : denom <> 0)
(irreducible : forall g n d : nat,
g * n = numer /\ g * d = denom -> g = 1),
P (mkRat numer denom denom_not_zero irreducible)) ->
forall r : Rat, P r *)

03 May, 2010

[Alloy][FM] Sample code: Addressbook

 [Coq][FM] Fomal Methods Forum #4でのAlloyチュートリアルの話。

 まず Alloy4 を各自インストールしておくのは前提。但し、JARファイル1つなので、JRE (Java5以上) が入っているコンピュータならば起動は難しくありません。

 表示されるWindowの左側入力欄に下記を打ち込みます。

sig Name, Addr {}
sig Book {
addr: Name -> lone Addr
}

 sigというのは普通のOO言語のクラス定義の様なもの --- なんですが、Alloy はOOなプログラミング言語ではないのでOOとのアナロジーは時に誤解を与えるかも。どちらかというと、RDBMSに Name, Addr という表を作ったと考えるといいかも。実際、Alloy は関係代数に基づいていますし。
 OO でいうところのインスタンスはAlloyではatomと言います。Alloyでインスタンスというのは、全てのatomから構成されるモデル(モデルの中には求める反例とかも含まれます)のことを指します。用語は間違わない様に。
 また、. (ドット演算子) もOO言語の属性やメソッドを指す様に勘違いしますが、join演算子で、RDBのjoinみたいな振る舞いをします。
 Bookのatomを仮に B0, B1 と書くと、Book は {(B0), (B1)} という集合です。( () はタプル、{}は集合を表す。)

 lone は 0 or 1 個を表す数量限定子です。他にも all (全ての)、some (1個以上)、one (1個)、no (0個) があります。lone Addr は、関数型言語的には Option Addrのようなものです。
 addr は Book の属性みたいに見えますが、これも関数型言語的には addr : Book -> Name -> Option Addr -> Prop みたいに考えると良いです。addr は (Book, Name, Addr) のタプル型の元の集合、例えば{(B0,N0,D0), (B1,N1,D1)}として表現されます。 (関係 R : R x y は (X,Y) の部分集合として表現出来る)
 Book = {(B0),(B1)} であると、ドットはjoin演算子なので(テンソルとかの添字の縮約の如く)ジョイン演算をします。Bookとaddrの共通のB0を見て(B0).(B0,N0,D1)=(N0,D0)の様に計算して、b=B0ならば、b.addr={(N0,D0)}, Book.addr={(N0,D0),(N1,D1)} となります。

 この時点で sig Book {...} の下に

pred show[] {}
run show for 3
と書いて Execute のボタンを押します。
 右側に Instance found と出るので Instance のリンクを押すと、atom 間の関係を示すダイヤグラムが表示されます。nextを押すと別のインスタンスが表示されます。
 for 3 と書いたので各 sig 毎に 3 atom づつです。

 次にモデルの性質をテストする事を考えます。addr を追加して削除すると元に戻る、という性質が成り立つ事をモデル検査で確認(=反例が見つからない事を確認)します。
 まず追加と削除を表す述語を定義します。述語なので戻り値は真偽値を返します。

pred add[disj b,b':Book, n:Name, a:Addr] {
b'.addr = b.addr + (n -> a)
}
pred del[disj b,b':Book, n:Name] {
b'.addr = b.addr - (n -> Addr)
}
 Alloyは手続き型言語ではないので、b'addrに何か破壊的代入が行われる事は無く、単にbとb'の間の関係を示しているだけです。disjはbとb'とが別atomであるという意味です。

 次いでテストしたい性質を書きます。

assert delUndoesAdd {
all b,b',b'':Book, n:Name, a:Addr |
add[b,b',n,a] and del[b',b'',n] implies b.addr = b''.addr
}
check delUndoesAdd for 5
 |はsuch thatみたいな意味、and, implisは論理演算の記号です。

 これを追加してexecuteすると「何故か」counterexampleが見つかったと表示されます。counterexampleをじっと眺めると、bとb'が同じBookであることが判ります。
 これは元々 b に (n -> a) が含まれていた場合、b, b' が同じになるからです。(集合に既にある要素を加えても同じ)
 assertの中身を

no n.(b.addr) and add[b,b',n,a] and del[b',b'',n] implies b.addr = b''.addr
に修正するとconterexampleが無くなります。

02 May, 2010

[Coq][FM] Fomal Methods Forum #4

 4/29に第4回FormalMethods勉強会を行いました。

 今回は
Alloy
 Alloyの教科書の例題(アドレス帳)を元に文法を勉強しました。大体文法は把握出来たかな。
 あとは自分で使ってみるべきなんだろうが、あまりモデリングとか仕事でやらないしなぁ。とりあえずパズルをAlloyで解く練習をするかなぁ。

CEGAR
 今回はCEGARという手法の話を聞きました。CEGARはCounterExample-Guided Abstraction Refinementの略で、モデル検査における状態数爆発を抑える為の一手法です。
 モデル検査で扱う検査の中に、到達可能性解析(エラー状態などの特定の状態にと到達するか否かの解析)があります。
 状態数を抑える方法としてモデルの抽象化(モデルの粗視化)があります。抽象化によって、エラー状態に遷移する可能性のある状態と、到達しない状態とが同一視されてしまうと、本当はエラー状態に到達しないにも関わらず、エラー状態に達すると誤って判定されます。
 CEGARは、エラー状態に到達する解に対して、解析(最弱事前条件?)を行って抽象化を修正(状態を分割)して再解析する手法です。エラー状態に達した場合にはそれが本当に達したのか、抽象化を改善して再度解析を実施します。
 原理だけ聞くと、当たり前の話と思えるのですが、実際に自動化するところを実装するのは難しそうというか見当が付かない。

Coq
 CoqはCertified Programming with Dependent TypeのChapter 1,2をざっと流して、Chapter 3を3.3まで読み終わりました。
 当日使用した資料 (PPT, Coqファイル) についてはGoogleグループ上の記事にリンクを纏めましたので、そちらから辿って下さい。
 本当はChapter 3を終わりたかったのですが終わらなかったのはちょっと残念でした。

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にも登録して頂くと良いと思います。

28 March, 2010

[Coq] Coq Course Materials at Nagoya Univ.

 名古屋大学の2009年度後期のGarrigue先生のCoqの講義の教材が公開されているので、Coqの勉強として解いてみた。
 全部は自力では解けず、答えを参照しつつ解いた部分もある。自習用には良い教材だと思った。
 解答とかcoqdocでHTML化したんだけど...宿題は公開するとまずいよね、やはり。2009年度後期はまだ終わってないし。
 とりあえずFormal Methods Forumの勉強会用に使えるかな?

28 February, 2010

[Joke] How do you trap a programmer in the shower?

redditのHow do you trap a programmer in the shower? (list.cs.brown.edu)経由。

[plt-scheme] OT: How do you trap a programmer in the shower?を翻訳してみました。

オチが判らなかった箇所が幾つかあって、それはつまり誤訳してる可能性が高いので、間違っていたら指摘して頂けると嬉しいです。

----

ある生徒が教室で1つ目のジョークを言い、この類にはもっと色々考えられる様な気がした。これは最初の試みなので批判や追加を歓迎する。代名詞の性別についてはご容赦を。男性と女性を入れ替えたりすべきかどうか自信が無いし、彼/彼女と書くのは変だし、複数形で書くと複数人でシャワーを浴びている様なニュアンスがでてしまって本意じゃないし。:-)

Todd

Schemeプログラマをシャワー室に閉じ込めるには?
シャンプーを一瓶渡せ。(Hand him a bottle of shampoo.)

Visual Basicプログラマをシャワー室に閉じ込めるには?
カーテンをオープンするウィジェットを隠せ。

BASICプログラマをシャワー室に閉じ込めるには?
彼のCommodore-64をタイルに固定する為にシリコン接着剤を使え。

アセンブラプログラマをシャワー室に閉じ込めるには?
えーと、まず彼はシャワーをビルドしないと。

Javaプログラマをシャワー室に閉じ込めるには?
どこかにexitShower()メソッドがあると彼を説得し、ドキュメント全体を精査し終わるまで笑ってやれ。

Pythonプログラマをシャワー室に閉じ込めるには?
Guidoがそこにいることを望んでいると彼に伝えろ。

Cプログラマをシャワー室に閉じ込めるには?
そんなことをするな。彼は風呂桶をオーバーフローさせて君の家のコントロールを奪うだろう。

----

以下はredditで追加された物を幾つかピックアップして紹介。

C#プログラマをシャワー室に閉じ込めるには?
> インテリセンスをオフにしろ。

PHPプログラマをシャワー室に閉じ込めるには?
1. その頃、PHPプログラマは芝生に裸で立ち、庭用ホースと驚く程沢山のアタッチメントを手にして、どうして他の全ての人がシャワーの方が良いと言っているのか理解出来ない。
2. PHPプログラマをシャワーの外に出そうとしても出来ない。全ての現実の仕事はシャワーの中でだけ起き、外でのことは単にアカデミックでエリートぶった連中のすることだと主張するだろう。
3. 必要ない。ドキュメントをチェックしないと呼ぶべきメソッドがopen_door(), door_open(), openDoor(), doorOpen() のどれなのか思い出せないから。

LISPプログラマをシャワー室に閉じ込めるには?
1. 冗談を。LISPプログラマはシャワーを浴びない。(don't _take_ showers : 副作用が無いという話?)
2. ")"キーを奪え。

Rubyプログラマをシャワー室に閉じ込めるには?
彼が中に入ったらCurtain#openメソッドにモンキーパッチを当てろ。追加で一つ引数を取る様にして、その引数が何なのかドキュメントに書いておかない。
> 彼は単に引数が不要になるモンキーパッチを当てて戻すだけだが、そのパッチは一回以上使うとsegfaultする様な代物だ。

Prologプログラマをシャワー室に閉じ込めるには?
1. Yes (ってオチが良く解らん)
2. inShower(Programmer) :- inShower(Programmer).

Haskellプログラマをシャワー室に閉じ込めるには?
1. < 圏論に関する馬鹿馬鹿しい程に長い小論を挿入せよ >
2. 何もする必要が無い。シャワーの水で洗って純粋になったならば、シャワーから出るには出力が必要だと気付くから。
3. 必要ない。彼は怠惰(lazy)なので外に出ない。

JavaScriptプログラマをシャワー室に閉じ込めるには?
1. 気にする必要は無い。同時に2つのシャワーは動作しないので、それを修正しようと時間を費やすから。
2. IE6のロゴをシャワーカーテンに張ってデバッグする様に頼め。

Erlangプログラマをシャワー室に閉じ込めるには?
出来ない。彼は自分自身をクローンして、別々のシャワーで全てのクローンに身体の別々の部位を洗わせる。もし閉じ込められたら単にそれを殺すだけだ。

C++プログラマをシャワー室に閉じ込めるには?
C++プログラマはシャワーをvoidポインタにキャストして、ドアの存在を消す。
> 「これは正しいやり方ではないがしかし...」という注釈を付けるのを忘れてるよ。

Perlプログラマをシャワー室に閉じ込めるには?
1. 彼はどうやったら自分のシャワーが動くのか思い出せない。
2. 彼の脱出用の針金の余りで、閉じたシャワーカーテンとシャワーの向きを固定しているダクトテープとをくっ付けておく。

Objective-Cプログラマをシャワー室に閉じ込めるには?
[[NSShower standardShower] addPerson:[NSObjectiveCProgrammer programmerWithRSI]];
SEL removeSelector = @selector(removePerson:);
Method removeMethod = class_getInstanceMethod([NSShower class], removeSelector);
removeMethod->method_imp = voidMethod;

14 February, 2010

[Coq][FM] Formal Methods Forum #1

 2010/02/08に第1回FormalMethods勉強会というのがありました。
 形式手法って、企業内の勉強会とか、あるいは有料の研修コースはあるのだけど (i.e. ビジネスになる、ということなんだろう)、無料のIT勉強会はほとんどありませんでした。
 「形式手法の勉強会欲しいよね〜」という話から勉強会を始めることになり、まず第1回は各自の持ちネタを持ち寄る感じで開催されました。
 私はCoqとWhy (INRIAで作っているプログラムの検証用ツール) の話をしました。発表資料をSlideshareに上げたので興味のある方はどうぞ。

 Coqに限らず、形式仕様記述 (B, Zとか)、あるいは各種モデル検査 (SPIN, Alloyとか)、Lightweight Formal Method (VDMとか) なんでもありの勉強会なので興味のある方はどうぞ。
 開催場所は新宿の豆蔵オフィスを利用させて頂く事が多いのではないかと思うが、Coqという意味では一度名古屋遠征したいなぁ。

Formal Methods Forum : 勉強会のGoogle group
fm-forum @ ウィキ : 勉強会のWiki

11 February, 2010

[Scala] Articles about Scala 2.8 on ITpro

 Scala 2.8に関する紹介記事をITproに書きました。ほぼ年末年始休暇を費やして書き、2010年の1,2月に分けて掲載。

第15回 Scala 2.8の新機能 (1)
第16回 Scala 2.8の新機能 (2) --- コレクションライブラリの再実装

 記事を書く機会を与えてくださった羽生田さんに感謝を。

31 January, 2010

[Scala] Hindley-Milner Type Inference

Hindley-Milnerの型推論を理解したいと思い、とりあえずScala by Exampleに載っているコードにデバッグプリントを大量に挿入して動かしてみました。
動かした結果を、将来の自分の為にメモしたものがHM.pdf、
動かしたソースはHM.scalaにあります。

アルゴリズム自体はHindley Milner Type Inference Algorithm (PS file)とか簡潔に書かれている。が、こうやってデバッグプリントを挟んで動かしてみないとなかなか判らない...。

17 December, 2009

[Scala] Talk on scala-be's "Step by Step Scala" on 12/22

 東京のScala勉強会であるscale-beの、12/22開催のStep by Step Scala [vol.06]@scala-be (ATNDへのリンク)で講師をすることになりました。
 今回は主として14章の「表明と単体テスト」のScalaCheckの使い方を中心に話をしようと思っています。ScalaCheckは「満たすべき性質を記述する」ことでテストケースを自動生成してくれる面白い単体テストツールですが、従来のxUnit系テストツールとはちょっと使い方が違うというのもあって、使い方を学ぶには良いのではないかと思います。
 参加者希望者は上記のATNDのリンクから申し込みをしていただければと思います。
 今回は日程が12/22と多くの忘年会と重なる日で、割と参加者は少なそうでちょっと残念。

29 November, 2009

[Scala] Scala Style Guide

Scala Style Guide (PDF)

Scalaのコーディングスタイルガイド。

[scala] Proposed Style Guideで議論されていて、Oderskyもコメントしてる。これが叩き台になって正式版が出たら訳すといいかなぁ。

23 November, 2009

[Scala][Book] Pro Scala: Monadic Design Patterns for the Web

APressからPro Scala: Monadic Design Patterns for the Webという本が出る様です。
今までの職業的プログラマの視野に入っていなかったが、実は実用的にも重要である概念の、
- Monadic design patterns
- Zippers and data type differentiation
- Delimited continuations
を考察するとのこと。

「モナド=デザインパターン」というのはあちこちで書かれていることだし、モナドに限らず圏論的視点で計算を考えようという話は檜山さんのブログで繰り返し出て来るテーマ(11/28開催のセミナーのコパスタを食べる余会はまだ空席があるようですよ)。
Delimited continuationということはScala 2.8対応だし、限定継続を一般向けに紹介した本は今までにあるのかなぁ?なんかそれだけでも価値があるよね。

なんか非常に楽しみな本ですね。

[Event] Recent Days

今週は、Step by Step Scala [vol.04]@scala-beで話をしたり、Haskellナイトを見に行ったり、Scala Hack-a-thon #1に参加したり、でした。

Step-by-Step Scalaは、(少なくとも何人かの方には)Swarmについて興味を持ってもらえた様なので満足。この先の仕事の状況がまだ確定しないのだけど、また機会があればScalaの講師を出来ればなぁ、とか。

Haskellナイトは、本の話がちょっと多過ぎたかなぁとは思うのだけど、出版合わせのイベントだから仕方がないのかなぁ。もうちょっとHaskellの話を聞きたかったかも。

Scala Hackathonは存在を知った時には既に満員になっていて参加を諦めてたんですが、当日朝見たらキャンセルが大量に出たみたいで慌てて家を出た次第。yuroyoroさんの作成したテキストが良く出来ていて、みんな黙々とそれを読んでいたのかな?かつ、質問はtwitterで主として行われていたみたい。懇親会も楽しく参加させて頂きました。

25 October, 2009

[Scala] Talk on scala-be's "Step by Step Scala" in November

東京のScala勉強会にscale-beというのがありまして、隔週の水曜or木曜の夜に新宿の豆蔵で開催しています。
内容はOderskyの「Scalaスケーラブルプログラミング」の章立てに沿って講師がScalaを解説するというもので、読書会の様にテキストに厳格に沿う訳ではありません。
参加者は、

  • 出来れば該当の本を持参して下さい。本の内容を逐一全部話す訳ではないですし。
  • 出来れば最近のScala実行系(Scala 2.7系)がインストールされたノートPCを持参して下さい。簡単な課題とか、実際に自分でインタプリタ上で動作確認したり、などがあるので。同様に、Scala APIドキュメント(JavaDoc)などをインストールしておくと良いです。(なお、会場はコンセントは利用可能ですが、インターネット接続は提供されません。)

あと、勉強会の後に懇親会も行っています。

で、11/4(Wed), 11/19(Thr)の2回は私が講師を担当する事になりました。それぞれ6,7章と8,9章の範囲について話す予定です。11/19の回にはテキストの内容からちょっと離れて最近のScalaの話題という事でSwarmの話をちょっとだけ紹介しようと思ってます。

なお、参加申し込みはATND上で行われ、ATNDへのリンクを含む開催案内は定期的にscala-be上で告知されます。
では、11月に勉強会会場でお会いしましょう。

---
追記:11/21

2回の発表で使用した資料はPDF形式でscala-be Google groupのファイル置き場で公開してます。

21 October, 2009

[Agda] Install Agda on Mac OS

Mac OS 10.5 の上にAgdaをインストールしました。基本的にはAgda Wikiを参考にしたのだけど、そのままではうまく動きませんでした。

  • Xcodeをインストール。DVDからなり、ADCのサイトなりから。最新版は10.6用なので古いのをインストール。
  • Mac Portsのインストール。/opt/local/binにPATHを通す。Mac Portsの準備が済んでいる人もsudo port -v selfupdateする。
  • GHCはportsからインストールすると古くて駄目なので、GHC download pageよりバイナリパッケージを導入。
  • sudo port install hs-cabalして、sudo cabal updateして、~/.cabal/binにPATHを通す。
  • sudo port install darcs
  • sudo cabal install happyして、sudo cabal install alex
  • sudo cabal install Agda-executable
  • cd ~/.cabal/binして、./agda-mode setup
  • emacsは薦められるままにAquamacsを導入する。
  • 下記を~/.emacsに追加。

    (setq load-path (cons "~/.cabal/share/Agda-2.2.4/emacs-mode/" load-path))
    (autoload 'agda2-mode "agda2-mode" "Major mode for Agda2 files" t)
    (unless (assoc "\\.agda" auto-mode-alist)
    (setq auto-mode-alist
    (nconc '(("\\.agda" . agda2-mode)
    ("\\.alfa" . agda2-mode)) auto-mode-alist)))

  • Aquamacsを起動し、~/test.agdaというファイルを作成して、

    module test where

    data Bool : Set where
      true : Bool
      false : Bool

    と書く(true, falseの前のインデント必須。単語や:の前後に空白があるのも確認。)
    C-c C-lで入力内容が処理され、文字が色分けされたら、agda2-modeが動いている。


---
追記: ~/.emacsについて念のため全部掲載。haskell-modeの設定も重要らしいので。

(setq load-path (cons "‾/.cabal/share/Agda-2.2.4/emacs-mode/" load-path))
(autoload 'agda2-mode "agda2-mode" "Major mode for Agda2 files" t)
(unless (assoc "¥¥.agda" auto-mode-alist)
(setq auto-mode-alist
(nconc '(("¥¥.agda" . agda2-mode)
("¥¥.alfa" . agda2-mode)) auto-mode-alist)))
(setq load-path (cons "‾/lib/elisp/haskell" load-path))
(setq auto-mode-alist
(append auto-mode-alist
'(("促促.[hg]s$" . haskell-mode)
("促促.hi$" . haskell-mode)
("促促.l[hg]s$" . literate-haskell-mode))))
(autoload 'haskell-mode "haskell-mode"
"Major mode for editing Haskell scripts." t)
(autoload 'literate-haskell-mode "haskell-mode"
"Major mode for editing literate Haskell scripts." t)
(add-hook 'haskell-mode-hook 'turn-on-haskell-decl-scan)
(add-hook 'haskell-mode-hook 'turn-on-haskell-doc-mode)
(add-hook 'haskell-mode-hook 'turn-on-haskell-indent)
(add-hook 'haskell-mode-hook 'turn-on-haskell-ghci)

(setq haskell-literate-default 'latex)
(setq haskell-doc-idle-delay 0)


(load-file (let ((coding-system-for-read 'utf-8))
(shell-command-to-string "agda-mode locate")))

16 October, 2009

[Agda] Agda Lectures at CVS

産業技術総合研究所の研修コース「 Agda による仕様記述」に参加しました。

以下は私の参加した10月の回の話。もしかしたら11月は10月の参加者の反応を見て内容が変更されるかもしれません。

一言で言うと
Coq, Agda のような定理証明系とか依存型の関数型言語とかに興味のある人が、概要を知るには良い二日半のコース。参加費無料なのもポイントが高い。お薦め。(開催が大阪なんで関西周辺以外の人にはちょっと厳しいが)

もう少し詳しく説明すると
「Agdaの持つ依存型の機能を使って、仕様の制約を型として表現すれば、機械的に検証出来るよね」というところに主眼を置いているみたいです。なので、定理証明の部分は従であり、主眼は仕様を書ける様になろう、ということのようです。
とはいえ、仕様の制約をAgdaで書く以上、書いた関数が仕様を満たしていることを示す必要がある訳ですが...。

が、まぁ当たり前と言えば当たり前なんですが、二日半ではAgdaを使いこなせるようにはならなかった...。まぁJavaだってHaskellだって二日半では使える様にはならない訳で、当然というか仕方が無い。なので、「Agdaがどんなものなのかの概説を聞く」ぐらいに考えていたほうが良いです。
とはいえ、Agdaに関する日本語の資料はほとんど無い現状で、テキストの説明を聞き、判らないところを教わりつつのハンズオン実習がある訳で、価値は大きい。

参加者は結局8名。堅苦しさの無い普通のIT系勉強会のようなフレンドリーな雰囲気で、質問しまくりみたいな。逆に言うと講義内容がまだきっちりと固まっている訳では無い感じです。
にわとり小屋でのプログラミング日記のCoqな今井さんとお近づきになれたのも良かったです。数人で2日目の夜に懇親会したのですが、最初からちゃんと計画して全員(講師の方にも声をかけて)でやれば良かったと反省。
#話に出た紫色の本は、"Handbook of Practical Logic and Automated Reasoning" (John Harrison, Cambridge Univ. Press, 2009) です>今井さん。

とにかく私は楽しかったです。代休消化で関西旅行した価値がありました。参加させて頂き、ありがとうございました>CVSの方々。

参加の前提について
募集要項には「プログラムを書いた経験(言語不問)、またはシステムやソフトウェアの設計に従事したことがあること。」「Emacs で文書を作成編集した経験があること。」と書いてあるんですが...。
うーむ、HaskellとかOCamlとかの類の型付き関数型言語の初歩的な知識があったほうがいいかも。モナドとか別に知らなくてもいいけど、簡単な再帰とかパターンマッチとか使って List の map とか length とかぐらい書けたほうが良いかもしれないです。そうでないと初日の演習問題でいきなり困るかも。あとはまぁ、ペアノの自然数( 3 = succ(succ(succ(zero))) みたいな話 ) も知ってた方が良いかなぁ。
ただ、参加者の関数型言語への習熟度を見て調整していたかも知れないので、11月はどうなるかは判らないです。

25 July, 2009

[Scala] sbt : simple-build-tool (1)

sbt (simple-build-tool) は Scala で書かれたビルドツールです。
これもscalaで書かれたDSL的なツールであり、プロジェクトのビルドの設定などをscalaで記述する事が出来たりします。

試しに使ってみて、使い方が判ったら追記していこうと思います。

★インストール

Setupのページに従い作業します。
私は MacOS ユーザなので Unix の指示に従い作業。

まずsbt-launcher-0.5.1.jar をダウンロードして指示通り~/binに置き、~/bin/sbt ファイルを作り chmod したりします。

% ls ~/bin
sbt sbt-launcher-0.5.1.jar
% cat ~/bin/sbt
java -Xmx256M -jar `dirname $0`/sbt-launcher-0.5.1.jar "$@"
%


★動作確認
空の (*.scala の無い) 作業ディレクトリに hw.scala を作ります。(main メソッドを探して自動で判断する都合上、無関係なソースがあると巧く動作しません。最初それで失敗しました。)

% ls
hw.scala
% cat hw.scala
object Hi { def main(args: Array[String]) { println("Hi!") } }
%


とりあえず下記の様に動きました。


[apple-3:~/work/scala/hw] miyamoto% ~/bin/sbt
Project does not exist, create new project? (y/N/s) : s
:: loading settings :: url = jar:file:/Users/miyamoto/bin/sbt-launcher-0.5.1.jar!/org/apache/ivy/core/settings/ivysettings.xml
:: retrieving :: sbt#boot
confs: [default]
2 artifacts copied, 0 already retrieved (9831kB/149ms)
:: retrieving :: sbt#boot
confs: [default]
3 artifacts copied, 0 already retrieved (3171kB/26ms)
[info] Building project scratch 1.0 using sbt.DefaultProject
[info] with sbt 0.5.1 and Scala 2.7.5
[info] No actions specified, interactive session started. Execute 'help' for more information.
> run
[info]
[info] == compile ==
[info] Source analysis: 1 new/modified, 0 indirectly invalidated, 0 removed.
[info] Compiling main sources...
[info] Compilation successful.
[info] Post-analysis: 2 classes.
[info] == compile ==
[info]
[info] == run ==
[info] Running Hi ...
Hi!
[info] == run ==
[success] Successful.
[info]
[info] Total time: 2 s
> quit
[info]
[info] Total session time: 14 s
%


この結果として下記の様にプロジェクトが生成されます。このあたりは Maven とかと同様な感じ。各プロジェクト毎にscala-compiler.jarを持ったりするのは、なんか富豪的だなぁ。

% ls -RCF
hw.scala project/ target/

./project:
boot/ build.properties

./project/boot:
scala-2.7.5/

./project/boot/scala-2.7.5:
lib/ sbt-0.5.1/ update.log

./project/boot/scala-2.7.5/lib:
scala-compiler.jar scala-library.jar

./project/boot/scala-2.7.5/sbt-0.5.1:
ivy-2.0.0.jar jsch-0.1.31.jar sbt_2.7.5-0.5.1.jar

./target:
analysis/ classes/

./target/analysis:
applications external hashes
dependencies generated_files tests

./target/classes:
Hi$.class Hi.class
%

31 May, 2009

[Scala] S-99: Ninety-Nine Scala Problems (P01-28)

S-99: Ninety-Nine Scala ProblemsのList編(P01-P28)の抄訳です。

  • アスタリスクの数は難易度です。

  • 効率も大事ですが、エレガントな回答を求めます。可能ならばより簡潔で、計算量が少なく、末尾再帰になっている回答を作りましょう。

  • Scalaの組み込み関数を使ってもOKです。が、使わないほうが勉強になります。

  • 答えが知りたければ、元の英語文書の各問題のリンクをクリックして下さい。



P01 (*) リストの最後の要素を求めよ。

scala> last(List(1, 1, 2, 3, 5, 8))
res0: Int = 8


P02 (*) リストの最後から二番目の要素を求めよ。

scala> penultimate(List(1, 1, 2, 3, 5, 8))
res0: Int = 5


P03 (*) リストのn番目の要素を求めよ。但しリストの最初の要素は0番目とする。

scala> nth(2, List(1, 1, 2, 3, 5, 8))
res0: Int = 2


P04 (*) リストの要素の数を求めよ。

scala> length(List(1, 1, 2, 3, 5, 8))
res0: Int = 6


P05 (*) リストを逆順にせよ。

scala> reverse(List(1, 1, 2, 3, 5, 8))
res0: List[Int] = List(8, 5, 3, 2, 1, 1)


P06 (*) リストが回文になっているか調べよ。

scala> isPalindrome(List(1, 2, 3, 2, 1))
res0: Boolean = true


P07 (**) ネストされたリスト構造を平坦化せよ。

scala> flatten(List(List(1, 1), 2, List(3, List(5, 8))))
res0: List[Any] = List(1, 1, 2, 3, 5, 8)


P08 (**) リスト要素の連続した重複物を除去せよ。もしリストの要素で繰り返し要素が含まれていたならば要素一つに置き換えよ。要素の順序は変えてはならない。

scala> compress(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
res0: List[Symbol] = List('a, 'b, 'c, 'a, 'd, 'e)


P09 (**) 連続した重複物を子リストに纏めよ。もしリストの要素が繰り返し要素ならば、別々の子リストに分割せよ。

scala> pack(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
res0: List[List[Symbol]] = List(List('a, 'a, 'a, 'a), List('b), List('c, 'c), List('a, 'a), List('d), List('e, 'e, 'e, 'e))


P10 (*) リストをランレングス・エンコードせよ。P09の結果を用いていわゆるランレングス・エンコーディングによるデータ圧縮法を実装せよ。連続した重複要素はタプル(N,E)にエンコードされる。但しNは要素Eの重複数。

scala> encode(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
res0: List[(Int, Symbol)] = List((4,'a), (1,'b), (2,'c), (2,'a), (1,'d), (4,'e))


P11 (*) 修正ランレングス・エンコーディング。P10の結果を修正し、もし要素に重複が無ければ単に要素を結果にコピーせよ。重複している要素だけを(N,E)の形に変換せよ。

scala> encodeModified(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
res0: List[Any] = List((4,'a), 'b, (2,'c), (2,'a), 'd, (4,'e))


P12 (**) ランレングス・エンコードされたリストをデコードせよ。P10の仕様で生成されたランレングス・エンコードされたリストを元の圧縮されていないものに戻せ。

scala> decode(List((4, 'a), (1, 'b), (2, 'c), (2, 'a), (1, 'd), (4, 'e)))
res0: List[Symbol] = List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e)


P13 (**) リストのランレングス・エンコーディング(直接解法)。いわゆるランレングス・エンコーディングを直接実装せよ。すなわち(P09のpackの様な)自分で書いた他のメソッドを使ってはならない。直接書く事。

scala> encodeDirect(List('a, 'a, 'a, 'a, 'b, 'c, 'c, 'a, 'a, 'd, 'e, 'e, 'e, 'e))
res0: List[(Int, Symbol)] = List((4,'a), (1,'b), (2,'c), (2,'a), (1,'d), (4,'e))


P14 (*) リスト要素を重複させよ。

scala> duplicate(List('a, 'b, 'c, 'c, 'd))
res0: List[Symbol] = List('a, 'a, 'b, 'b, 'c, 'c, 'c, 'c, 'd, 'd)


P15 (**) 指定した個数、リスト要素を重複させよ。

scala> duplicateN(3, List('a, 'b, 'c, 'c, 'd))
res0: List[Symbol] = List('a, 'a, 'a, 'b, 'b, 'b, 'c, 'c, 'c, 'c, 'c, 'c, 'd, 'd, 'd)


P16 (**) 毎N番目の要素を除去せよ。

scala> drop(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
res0: List[Symbol] = List('a, 'b, 'd, 'e, 'g, 'h, 'j, 'k)


P17 (*) リストを二つに分割せよ。前半の長さは与えられるものとする。結果はタプルで返す。

scala> split(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
res0: (List[Symbol], List[Symbol]) = (List('a, 'b, 'c),List('d, 'e, 'f, 'g, 'h, 'i, 'j, 'k))


P18 (**) リストのスライスを抽出せよ。二つの添字 i と j が与えられたとき、スライスとは元のリストの i 番目の要素を含むが j 番目の要素を含まないリストである。要素は0番目から始まるとする。

scala> slice(3, 7, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
res0: List[Symbol] = List('d, 'e, 'f, 'g)


P19 (**) リストの要素をn個左ローテートせよ。

scala> rotate(3, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
res0: List[Symbol] = List('d, 'e, 'f, 'g, 'h, 'i, 'j, 'k, 'a, 'b, 'c)

scala> rotate(-2, List('a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i, 'j, 'k))
res1: List[Symbol] = List('j, 'k, 'a, 'b, 'c, 'd, 'e, 'f, 'g, 'h, 'i)


P20 (*) リストのk番目の要素を除去せよ。除去されたリストと除去した要素をタプルで返せ。要素は0番目から始まるとする。

scala> removeAt(1, List('a, 'b, 'c, 'd))
res0: (List[Symbol], Symbol) = (List('a, 'c, 'd),'b)


P21 (*) リストの指定された場所に要素を追加せよ。

scala> insertAt('new, 1, List('a, 'b, 'c, 'd))
res0: List[Symbol] = List('a, 'new, 'b, 'c, 'd)


P22 (*) 与えられた範囲の整数のリストを作れ。

scala> range(4, 9)
res0: List[Int] = List(4, 5, 6, 7, 8, 9)


P23 (**) リストから指定された数だけ値をランダムに選択せよ。(ヒント:P20を使え)

scala> randomSelect(3, List('a, 'b, 'c, 'd, 'f, 'g, 'h))
res0: List[Symbol] = List('e, 'd, 'a)


P24 (*) ロト:1〜Mからn個の異なるランダムな値を選べ。

scala> lotto(6, 49)
res0: List[Int] = List(23, 1, 17, 33, 21, 37)


P25 (*) 要素のランダムな順列を作成せよ。(ヒント:P23を使え)

scala> randomPermute(List('a, 'b, 'c, 'd, 'e, 'f))
res0: List[Symbol] = List('b, 'a, 'd, 'c, 'e, 'f)


P26 (**) n要素数のリストからk個の異なるオブジェクトを取り出す組み合わせを生成せよ。12人から3人の委員会を作る方法は何通りだろうか?答えは C(12,3)=220通り(C(n,k)はよく知られた二項係数)である。数学者にとってはこれで十分であるが、我々は本当に全ての解を生成したい。

scala> combinations(3, List('a, 'b, 'c, 'd, 'e, 'f))
res0: List[List[Symbol]] = List(List('a, 'b, 'c), List('a, 'b, 'd), List('a, 'b, 'e), ...


P27 (**) 集合の要素を、互いに素な部分集合に纏めよ。
a) 9人の人をそれぞれ2,3,4人の3グループに纏める方法は何通りか?全ての組み合わせを生成する関数を書け。

scala> group3(List("Aldo", "Beat", "Carla", "David", "Evi", "Flip", "Gary", "Hugo", "Ida"))
res0: List[List[List[String]]] = List(List(List(Aldo, Beat), List(Carla, David, Evi), List(Flip, Gary, Hugo, Ida)), ...

b) 上の問題を一般化してグループの大きさのリストを与えるとグループのリストを与える様にせよ。グループメンバーの順列は求めていない、すなわち((Aldo,Beat),...)は((Beat,Aldo),...)と同じ解である。しかし、((Aldo,Beat),(Carla,David),...)は((Carla,David),(Aldo,Beat),...)と異なる解である。

scala> group(List(2, 2, 5), List("Aldo", "Beat", "Carla", "David", "Evi", "Flip", "Gary", "Hugo", "Ida"))
res0: List[List[List[String]]] = List(List(List(Aldo, Beat), List(Carla, David), List(Evi, Flip, Gary, Hugo, Ida)), ...

この組み合わせ問題に関して知りたければ離散数学の良い本で「多項係数」について調べよ。
P28 (**) リストのリストを長さでソートせよ。
a) 要素がリストであるリストを考える。そのリストの要素を長さでソートする、すなわち短いリストを前に、長いリストを後にする。

scala> lsort(List(List('a, 'b, 'c), List('d, 'e), List('f, 'g, 'h), List('d, 'e), List('i, 'j, 'k, 'l), List('m, 'n), List('o)))
res0: List[List[Symbol]] = List(List('o), List('d, 'e), List('d, 'e), List('m, 'n), List('a, 'b, 'c), List('f, 'g, 'h), List('i, 'j, 'k, 'l))

b) 次に同様にリストを長さでソートするが、今回は長さの頻度でソートする。すなわち稀な長さのものを前に、頻度の高い長さのものを後ろにする。例の場合、長さ4と1のリストはただ1度しか現れない。3番目と4番目は長さ3のリスト2つである。最後に3つの最も頻度の高い長さ2のリストが現れる。

scala> lsortFreq(List(List('a, 'b, 'c), List('d, 'e), List('f, 'g, 'h), List('d, 'e), List('i, 'j, 'k, 'l), List('m, 'n), List('o)))
res1: List[List[Symbol]] = List(List('i, 'j, 'k, 'l), List('o), List('a, 'b, 'c), List('f, 'g, 'h), List('d, 'e), List('d, 'e), List('m, 'n))

22 May, 2009

[Scala] Sudoku in Scala

Scalaユーザ会5/22(金)19:00-21:00@新宿三井ビル3で話をする予定の「Scalaで数独を解く」話です。

ScalaSudoku.pdf : プレゼン資料
Sudoku.scala : ソースコード

20 May, 2009

[Joke] Translation of "A Brief, Incomplete, and Mostly Wrong History of Programming Languages"

A Brief, Incomplete, and Mostly Wrong History of Programming Languagesの翻訳です。面白かったので翻訳してみました。

「簡潔で不完全でほとんど間違っているプログラミング言語の歴史」

1801 - Joseph Marie Jacquardが、織機にパンチカードで命令することで、タペストリーに「hello, world」を織り込んだ。(しかし)末尾再帰やコンカレンシーの欠如、あるいは適切に大文字が使用されていないため、当時のRedditerたちは感銘を覚え無かった。

1842 - Ada Lovelaceが最初のプログラムを書いた。その過程に於いて、コードを走らせる実際のコンピュータを持っていないという些細な困難に妨げられた。後のエンタープライズアーキテクトたちは、UMLでプログラムする為に、彼女のテクニックを再習得した。

1936 - Alan Turingが、(将来に亘る)全てのプログラミング言語を発明した。しかし彼がその特許をとる前に、英国情報部は彼を007にするために強制徴募した。

1936 - Alonzo Churchもまた、(将来に亘る)全てのプログラミング言語をさらにうまく発明した。彼のλ算法はC言語に十分似ていない為に無視された。この批判は、当時まだCが発明されていないという事実にも関わらず生じた。

1940年代 - 結線とスイッチによって様々な「コンピュータ」が「プログラム」された。「タブ vs 空白」の論戦を避けるために、技術者たちはこの方式を採用した。

1957 - John BackusとIBMがFORTRANを作った。IBMにもFORTRANにも面白いところは何も無い。青いネクタイを着用せずFORTRANを書くのは文法エラーである。

1958 - John McCarthyとPaul GrahamがLISPを発明する。戦後の戦略的括弧備蓄の枯渇による高コストの為、LISPは決してポピュラーにはならなかった[1]。ポピュラーでは無いにも関わらず、LISP (今では "Lisp" あるいは時には "Arc") は「再帰と他人を見下すことなどの重要なアルゴリズム技法」に於ける影響度の高い言語であり続けている。

1959 - L. Ron Hubbardとの賭けに負けた後に、Grace Hopperを含む何人かのサディスト達が「大文字化された定型文志向言語」 (Capitalization Of Boilerplate Oriented Language = COBOL) を発明する。後に、Hooper提督のCOBOLの業績に対する見当外れで性差別的な報復として、Rubyコンファレンスでは嫌女性的題材が取り上げられる。

1964 - John KemenyとThomas Kurtzが、非計算機科学者の為の非構造化プログラミング言語であるBASICを作った。

1965 - KemenyとKurtzはGO TO 1964.

1970 - Guy SteeleとGerald SussmanがSchemeを作った。彼らの著作は「Lambda the Ultimate」の一連の論文をもたらし、「究極の台所用品ラムダ」にその頂点を迎えた。この論文はロングランの基礎となったが、深夜のインフォマーシャルとしては究極的に失敗であった。Javaがラムダを持たないことによってラムダをポピュラーにするまでラムダは比較的目立たないところへと左遷された。

1970 - Niklaus Wirthが手続き型言語のPascalを作った。Pascalは直ちに批難されたが、"x := x + y"という構文を、より親しみやすいC的な"x = x + y"の代わりに使用したためであった。この批判は、当時まだCが発明されていないという事実にも関わらず生じた。

1972 - Dennis Ritchieは前後を同時に撃つことの出来る強力な銃を発明した。発明のもたらした多くの死者及び障害者に満足することなく、彼はCとUnixを発明した。

1972 - Alain Colmerauerは論理型言語Prologをデザインした。彼の目標は二歳児の知性を持った言語を作ることであった。全てのクエリに「No」と答えるPrologセッションを示すことによって、目標に達したことを証明した。

1973 - Robin MilnerはM&M型理論に基づく言語のMLを作った。MLの子供として形式仕様意味論を持つSMLが生まれた。形式意味論の形式意味論を質問されてMilnerの頭は爆発した。ML一家の他の良く知られた言語にはOCaml, F#, Visual Basicがある。

1980 - Alan kayはSmalltalkを作り、用語「オブジェクト指向」を発明した。その意味を聞かれると彼は「Smalltalkのプログラムは単にオブジェクトである」と答えた。オブジェクトは何から作られるのかを聞かれると彼は「オブジェクトだ」と答えた。再度質問されると彼は云った。「だからさ、下の下まで全部オブジェクトなんだってば。亀にたどり着くまでは。」

1983 - Bjarne Stroustrupは耳にしたもの全てをCにねじ止めすることでC++を作った。その結果、言語は非常に複雑になり、プログラムをSkynet人工知能でコンパイルするために未来へ送らねばならなかった。ビルド時間は犠牲となった。Skynetがサービスを提供し続けた動機は依然としてはっきりしないが、未来からの広報担当はオーストリア訛りで単調に「気にするようなことは何も無い、ベイビー」と云った。Skynetはバッファーオーバーランを飾り立てたものに過ぎないという推測もある。

1986 - Brad CoxとTom LoveがObjective-Cを作り、アナウンスした。「この言語はCのメモリ安全性とSmalltalkの素晴らしい実行速度が結びついたものです。」現代の歴史家たちは二人が失読症であったと疑っている。

1987 - Larry Wallが眠気を催し、キーボードに額をぶつけた。目を覚ましたとき、Larry Wallのモニターの上の文字列はランダムなのではなく、神が預言者Larry Wallにデザインすることを欲しているプログラミング言語のサンプルプログラムだと悟った。Perlが生まれた。

1990 - Simon Peyton-Jones, Paul Hudak, Philip Wadler, Ashton Kutcher そして「動物の倫理的扱いを求める人々の会」からなる委員会は、純粋非正格関数型言語Haskell を作った。副作用を制御するためモナドを使用する複雑さの為、Haskellは抵抗を受けた。Wadler は批判を和らげるために説明した。「モナドは自己準同系ファンクタの圏のモノイドなんだ。何か問題が?」

1991 - オランダ人プログラマのGuido van Rossumが謎の手術の為にアルゼンチンへと旅行した。頭部に大きな傷を負って帰国し、Pythonを発明し、多数の賛同者によって終身独裁者に任じられ、世界に対して「あることをするのにひとつしかやり方がない」と報じた。ポーランドは神経質になっている。

1995 - 「Mad Matz」こと、まつもとゆきひろは、漠然とした特定されない終末を避けるためにRubyを作ったが、その終末においてはオーストラリアはモヒカン戦士とティナ=ターナーが支配する砂漠となる。その言語は後にRuby on Railsと、真の発明者David Heinemeier Hanssonによって改名された。[まつもとがRubyと呼ばれる言語を発明した云々は実際には起きておらず、この記事の次の改訂時に削除されるべきだ - DHH].

1995 - Brendan Eichはプログラミング言語設計で起きた全ての失敗について読み、自分でも幾つか発明し、LiveScriptを作成した。後にその言語は、Javaの人気に肖る為、JavaScriptと改名された。後になっても、皮膚病の人気に肖る為にECMAScriptと改名された。

1996 - James GoslingはJavaを発明した。Javaは比較的冗長で、ガベージコレクションをし、クラスベースで、静的型付けで、シングルディスパッチで、単一実装継承と複数インタフェース継承のオブジェクト指向な言語である。SunはJavaの新規性を宣伝した。

2001 - Anders HejlsbergがC#を発明した。C#は比較的冗長で、ガベージコレクションをし、クラスベースで、静的型付けで、シングルディスパッチで、単一実装継承と複数インタフェース継承のオブジェクト指向の言語である。MicrosoftはC#の新規性を宣伝した。

2003 - 酔っ払ったMartin Oderskyは誰かのピーナツバターが別の人のチョコレートにくっつくというReeseのピーナツバターカップのCMを見て着想を得た。彼はオブジェクト指向と関数型言語の作り上げたものを統合する言語Scalaを作った。これを見た両派閥とも激怒し、それぞれ直ちに聖戦を宣告した。

Footnotes

1. 計算機科学にとっては幸運なことに、中括弧と山括弧の供給は潤沢であった。
2. Catch as catch can - Verity Stob

03 May, 2009

[Scala] Jersey with Scala + Jetty

Jersey は JAX-RS (Java API for RESTful Web Service)のreference実装です。
これをScalaで動かしてみます。

1. Libraries : Jetty 6.1.17. Jersey 1.0.3 を使用しました。下記をEclipseプロジェクトのreference librariesに登録

Jetty : jetty-XX.jar, jetty-util-XX.jar, servlet-api-XX.jar
Jersey : jsr311-api-XX.jar, jersey-core-XX.jar, jersey-server-XX.jar, asm-XX.jar

2. test.jersey.JerseyTest.scala : メインルーチンです

package test.jersey

import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

import org.mortbay.jetty.Server
import org.mortbay.jetty.nio.SelectChannelConnector
import org.mortbay.jetty.servlet.Context
import org.mortbay.jetty.servlet.ServletHolder

import com.sun.jersey.spi.container.servlet.ServletContainer

object JerseyTest {
def main(args: Array[String]) {
val server = new Server(8080)
val connector = new SelectChannelConnector()
server.addConnector(connector)

val holder:ServletHolder = new ServletHolder(classOf[ServletContainer])
holder.setInitParameter(
"com.sun.jersey.config.property.resourceConfigClass",
"com.sun.jersey.api.core.PackagesResourceConfig")
holder.setInitParameter(
"com.sun.jersey.config.property.packages",
"test.jersey.resource")
// URLをクラスにマッピングする為のpackage名

val context = new Context(server, "/", Context.SESSIONS)
context.addServlet(holder, "/*")

server.start()
server.join()
}
}


3. test.jersey.resource : /helloworld に対応するリソース

package test.jersey.resource

import javax.ws.rs.GET
import javax.ws.rs.Produces
import javax.ws.rs.Path

@Path("/helloworld")
class HelloWorldResource {
@GET
@Produces(Array("text/plain"))
def getMessage:String = "Hello, World"
}


4. テスト
Eclipseでtest.jersey.JerseyTest をアプリケーションとして実行させます。

2009-05-03 22:56:18.064::INFO: Logging to STDERR via org.mortbay.log.StdErrLog
2009-05-03 22:56:18.123::INFO: jetty-6.1.17
2009-05-03 22:56:18.205::INFO: Started SocketConnector@0.0.0.0:8080
2009-05-03 22:56:18.226::INFO: Started SelectChannelConnector@0.0.0.0:55736

次いでアクセス

% telnet localhost 8080
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET /helloworld HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/plain
Server: Jetty(6.1.17)

Hello, WorldConnection closed by foreign host.
%



2009/05/03 22:57:08 com.sun.jersey.api.core.PackagesResourceConfig init
情報: Scanning for root resource and provider classes in the packages:
test.jersey.resource
2009/05/03 22:57:08 com.sun.jersey.api.core.PackagesResourceConfig init
情報: Root resource classes found:
class test.jersey.resource.HelloWorldResource
2009/05/03 22:57:08 com.sun.jersey.api.core.PackagesResourceConfig init
情報: Provider classes found:

[Scala] Scala Servlet with Jetty6

Jetty + ScalaでServletを書いてみました。

1. Projectの作成
Eclipseで普通にScala Projectを作ります。
ScalaServletという名前のScala Projectを作りました。

2. Jetty6 の入手
JettyのサイトからJetty6を入手します。私がダウンロードしたのはJetty 6.1.17でした。
zipを解凍し、jetty-6.1.17.jar, jetty-util-6.1.17.jar, servlet-api-2.5-20081211.jar をプロジェクトにimportします。

3. ソースを書く
パッケージtest.jettyを作って、下記の様なJettyTest.scalaというファイルを作成します

package test.jetty

import javax.servlet.ServletException
import javax.servlet.http.HttpServlet
import javax.servlet.http.HttpServletRequest
import javax.servlet.http.HttpServletResponse

import org.mortbay.jetty.Server
import org.mortbay.jetty.nio.SelectChannelConnector
import org.mortbay.jetty.servlet.ServletHandler

object JettyTest {

def main(args: Array[String]) {
val server = new Server(8080)
val connector = new SelectChannelConnector()
server.addConnector(connector)

val handler = new ServletHandler()
handler.addServletWithMapping(HelloServlet.getClass, "/")
server.addHandler(handler)

server.start()
server.join()
}
}

object HelloServlet extends HttpServlet {
override def doGet(req:HttpServletRequest, resp:HttpServletResponse) {
val out = resp.getWriter
resp.setContentType("text/html")
out.println("<html><body>Hello, World!</body></html>")
}
}


4.サーバ起動
上記のJettyTest.scalaを普通にScala Applicationとして起動します。
コンソールに下記の様に表示されます。

2009-05-03 02:20:29.316::INFO: Logging to STDERR via org.mortbay.log.StdErrLog
2009-05-03 02:20:29.358::INFO: jetty-6.1.17
2009-05-03 02:20:29.392::INFO: Started SocketConnector@0.0.0.0:8080
2009-05-03 02:20:29.413::INFO: Started SelectChannelConnector@0.0.0.0:52134


5.アクセス
ブラウザからhttp://localhost:8080/で確認してもOKですがコンソールから確認。

% telnet localhost 8080
Trying ::1...
telnet: connect to address ::1: Connection refused
Trying fe80::1...
telnet: connect to address fe80::1: Connection refused
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
GET / HTTP/1.0

HTTP/1.1 200 OK
Content-Type: text/html; charset=iso-8859-1
Content-Length: 40
Server: Jetty(6.1.17)

<html><body>Hello, World!</body></html>
Connection closed by foreign host.
%