Lists: 直積、リスト、オプション
Inductive による型定義では、各構成子は任意の個数の引数を取ることができました。
true や O のように引数のないもの、 S のようにひとつのもの、また、ふたつ以上取るものも nybble や以下のように定義することができます。
ペアの第一要素と第二要素を取り出す関数を定義します。
Definition fst (p : natprod) : nat :=
match p with
| pair x y => x
end.
Definition snd (p : natprod) : nat :=
match p with
| pair x y => y
end.
Compute (fst (pair 3 5)).
こうして定義したペアの新しい記法(notation)は、式だけでなくパターンマッチに使うこともできます。
Compute (fst (3,5)).
Definition fst' (p : natprod) : nat :=
match p with
| (x,y) => x
end.
Definition snd' (p : natprod) : nat :=
match p with
| (x,y) => y
end.
Definition swap_pair (p : natprod) : natprod :=
match p with
| (x,y) => (y,x)
end.
Note that pattern-matching on a pair (with parentheses: (x, y))
is not to be confused with the "multiple pattern" syntax
(with no parentheses: x, y) that we have seen previously.
The above examples illustrate pattern matching on a pair with
elements x and y, whereas minus below (taken from
Basics) performs pattern matching on the values n
and m.
Fixpoint minus (n m : nat) : nat :=
match n, m with
| O , _ => O
| S _ , O => n
| S n', S m' => minus n' m'
end.
The distinction is minor, but it is worth knowing that they
are not the same. For instance, the following definitions are
ill-formed:
Definition bad_fst (p : natprod) : nat :=
match p with
| x, y => x
end.
Definition bad_minus (n m : nat) : nat :=
match n, m with
| (O , _ ) => O
| (S _ , O ) => n
| (S n', S m') => bad_minus n' m'
end.
それでは、数のペアに関する簡単な事実をいくつか証明してみましょう。
一定の形式で書いておくと、単に reflexivity(と組み込みの簡約)だけで証明できます。
Theorem surjective_pairing' : forall (n m : nat),
(n,m) = (fst (n,m), snd (n,m)).
Proof.
reflexivity. Qed.
しかし、補題を以下のようにより自然な書き方をした場合、reflexivityでは足りません。
Theorem surjective_pairing : forall (p : natprod),
p = (fst p, snd p).
Proof.
intros p. destruct p as [n m]. simpl. reflexivity. Qed.
☐
☐
ペアの定義を一般化することで、数の「リスト(list)」は次のように表すことができます。
「リストは、空のリストであるか、または数と他のリストをペアにしたものである。」
たとえば、次の定義は要素が三つのリストです
ペアの場合と同じく、リストをプログラミング言語で馴染んだ記法で書くことができると便利でしょう。
次の宣言では :: を中置の cons 演算子として使えるようにし、角括弧をリストを構成するための外置(outfix)記法として使えるようにしています。
Notation "x :: l" := (cons x l)
(at level 60, right associativity).
Notation "[ ]" := nil.
Notation "[ x ; .. ; y ]" := (cons x .. (cons y nil) ..).
この宣言を詳細まで理解する必要はありませんが、興味のある読者のために簡単に説明しておきます。
right associativity アノテーションは複数の :: を使った式にどのように括弧を付けるか指示するものです。
例えば、次の三つの宣言はすべて同じ意味に解釈されます。
Definition mylist1 := 1 :: (2 :: (3 :: nil)).
Definition mylist2 := 1 :: 2 :: 3 :: nil.
Definition mylist3 := [1;2;3].
at level 60 の部分は :: を他の中置演算子といっしょに使っている式にどのように括弧を付けるかを指示するものです。
例えば、 + を plus に対する level 50 の中置記法として定義したので、
Notation "x + y" := (plus x y)
(at level 50, left associativity).
+ は :: よりも強く結合し、 1 + 2 :: [3] は期待通り、 1 + (2 :: [3]) ではなく (1 + 2) :: [3] と構文解析されます。
(.v ファイルを読んでいるときには "1 + 2 :: [3]" のような書き方は少し読みにくいように感じるでしょう。
内側の 3 の左右の角括弧はリストを表すものですが、外側の括弧は "coqdoc" 用の命令で、角括弧内の部分をそのままのテキストではなく Coq のコードとして表示するよう指示するものです。
この角括弧は生成された HTML には現れません。)
上の二番目と三番目の Notation 宣言は標準的なリストの記法を導入するためのものです。
三番目の Notation の右辺は、 n 引数の記法を二項構成子の入れ子に変換する記法を定義するための Coq の構文の例です。
Notation "x + y" := (plus x y)
(at level 50, left associativity).
Fixpoint repeat (n count : nat) : natlist :=
match count with
| O => nil
| S count' => n :: (repeat n count')
end.
length 関数はリストの長さを計算します。
app 関数はふたつのリストを連結(append)します。
Fixpoint app (l1 l2 : natlist) : natlist :=
match l1 with
| nil => l2
| h :: t => h :: (app t l2)
end.
app はこの後でよく使うので、中置演算子を用意しておくと便利でしょう。
Notation "x ++ y" := (app x y)
(right associativity, at level 60).
Example test_app1: [1;2;3] ++ [4;5] = [1;2;3;4;5].
Proof. reflexivity. Qed.
Example test_app2: nil ++ [4;5] = [4;5].
Proof. reflexivity. Qed.
Example test_app3: [1;2;3] ++ nil = [1;2;3].
Proof. reflexivity. Qed.
もうふたつリストを使った例を見てみましょう。
hd 関数はリストの最初の要素(先頭—— head)を返し、 tl は最初の要素を除いたもの(後ろ―― tail)を返します。
空のリストには最初の要素はありませんから、その場合に返す値を引数として渡しておかなければなりません。
Definition hd (default:nat) (l:natlist) : nat :=
match l with
| nil => default
| h :: t => h
end.
Definition tl (l:natlist) : natlist :=
match l with
| nil => nil
| h :: t => t
end.
Example test_hd1: hd 0 [1;2;3] = 1.
Proof. reflexivity. Qed.
Example test_hd2: hd 0 [] = 0.
Proof. reflexivity. Qed.
Example test_tl: tl [1;2;3] = [2;3].
Proof. reflexivity. Qed.
練習問題: ★★, standard, recommended (list_funs)
Fixpoint nonzeros (l:natlist) : natlist
. Admitted.
Example test_nonzeros:
nonzeros [0;1;0;2;3;0;0] = [1;2;3].
Admitted.
Fixpoint oddmembers (l:natlist) : natlist
. Admitted.
Example test_oddmembers:
oddmembers [0;1;0;2;3;0;0] = [1;3].
Admitted.
Definition countoddmembers (l:natlist) : nat
. Admitted.
Example test_countoddmembers1:
countoddmembers [1;0;3;1;4;5] = 4.
Admitted.
Example test_countoddmembers2:
countoddmembers [0;2;4] = 0.
Admitted.
Example test_countoddmembers3:
countoddmembers nil = 0.
Admitted.
☐
練習問題: ★★★, advanced (alternate)
Fixpoint alternate (l1 l2 : natlist) : natlist
. Admitted.
Example test_alternate1:
alternate [1;2;3] [4;5;6] = [1;4;2;5;3;6].
Admitted.
Example test_alternate2:
alternate [1] [4;5;6] = [1;4;5;6].
Admitted.
Example test_alternate3:
alternate [1;2;3] [4] = [1;4;2;3].
Admitted.
Example test_alternate4:
alternate [] [20;30] = [20;30].
Admitted.
☐
バッグ(bag、または多重集合—— multiset)は集合のようなものですが、それぞれの要素が一度きりではなく複数回現れることのできるようなものを言います。
数のバッグの表現としては数のリストがありえます。
下の証明はすべて reflexivity だけでできます。
Example test_count1: count 1 [1;2;3;1;4;1] = 3.
Admitted.
Example test_count2: count 6 [1;2;3;1;4;1] = 0.
Admitted.
多重集合の sum (直和、または非交和)は集合の union (和)と同じようなものです。
sum a b は a と b の両方の要素を持つ多重集合です。
(数学者は通常、多重集合の union に少し異なる定義として、和(sum)を使う代わりに最大(max)を使ったものを与えます。
それが、この関数の名前を union にしなかった理由です。)
sum のヘッダには引数の名前を与えませんでした。
さらに、 Fixpoint ではなく Definition を使っています。
ですから、引数に名前がついていたとしても再帰的な処理はできません。
問題をこのように設定したのは、 sum を(定義済みの関数を使うといった)別の方法で定義できないか考えさせるためです。
Definition sum : bag -> bag -> bag
. Admitted.
Example test_sum1: count 1 (sum [1;2;3] [1;4;1]) = 3.
Admitted.
Definition add (v:nat) (s:bag) : bag
. Admitted.
Example test_add1: count 1 (add 1 [1;4;1]) = 3.
Admitted.
Example test_add2: count 5 (add 1 [1;4;1]) = 0.
Admitted.
Definition member (v:nat) (s:bag) : bool
. Admitted.
Example test_member1: member 1 [1;4;1] = true.
Admitted.
Example test_member2: member 2 [1;4;1] = false.
Admitted.
☐
remove_one を削除しようとしている数の入っていないバッグに適用した場合は、同じバッグを変更せずに返す。
(この練習問題はオプション問題ですが、後ろの発展問題で remove_one の定義を必要とするものがあります。)
Fixpoint remove_one (v:nat) (s:bag) : bag
. Admitted.
Example test_remove_one1:
count 5 (remove_one 5 [2;1;5;4;1]) = 0.
Admitted.
Example test_remove_one2:
count 5 (remove_one 5 [2;1;4;1]) = 0.
Admitted.
Example test_remove_one3:
count 4 (remove_one 5 [2;1;4;5;1;4]) = 2.
Admitted.
Example test_remove_one4:
count 5 (remove_one 5 [2;1;5;4;5;1;4]) = 1.
Admitted.
Fixpoint remove_all (v:nat) (s:bag) : bag
. Admitted.
Example test_remove_all1: count 5 (remove_all 5 [2;1;5;4;1]) = 0.
Admitted.
Example test_remove_all2: count 5 (remove_all 5 [2;1;4;1]) = 0.
Admitted.
Example test_remove_all3: count 4 (remove_all 5 [2;1;4;5;1;4]) = 2.
Admitted.
Example test_remove_all4: count 5 (remove_all 5 [2;1;5;4;5;1;4;5;1;4]) = 0.
Admitted.
Fixpoint subset (s1:bag) (s2:bag) : bool
. Admitted.
Example test_subset1: subset [1;2] [2;1;4;1] = true.
Admitted.
Example test_subset2: subset [1;2;2] [2;1;4;1] = false.
Admitted.
☐
練習問題: ★★★, standard, recommended (bag_theorem)
数の場合と同じく、リスト処理関数についての簡単な事実はもっぱら簡約のみで証明できることがあります。
たとえば、次の定理は reflexivity で行われる簡約だけで証明できます。
これは、 [] が app の定義における「被検査体(scrutinee)」(パターンマッチでその値が「検査(scrutinize)」される式 / 訳注:matchとwithの間に書いた式)に代入され、パターンマッチが簡約できるようになるからです。
またこれも数の場合と同じように、未知のリストの形(空であるかどうか)に応じた場合分けも有効です。
Theorem tl_length_pred : forall l:natlist,
pred (length l) = length (tl l).
Proof.
intros l. destruct l as [| n l'].
-
reflexivity.
-
reflexivity. Qed.
ここで、 nil の場合がうまく行くのは、 tl nil = nil と定義したからです。
ここでは、 destruct タクティックの as で n と l' のふたつの名前を導入しました。
これは、リストの cons 構成子が引数をふたつ(構成するリストの頭部と尾部)取ることに対応しています。
ただし、リストに関する興味深い定理の証明には、帰納法が必要になるのが普通です。
単にスクリプトを「読んでいる」だけでは大きな進歩は望めません!
各スクリプトを実際に Coq で一歩一歩進め、各ステップがその証明にどのようにかかわっているか考えることが大切です。
そうしなければ、演習に取り組んでも何の意味もありません。
もうおわかりですよね?
natlist のようなデータ型に対して帰納法で証明をするのは、普通の自然数に対する帰納法よりも馴染みにくさを感じたことでしょう。
しかし、考え方は同様に単純です。
Inductive 宣言では、宣言した構成子を使って構築できる値の集合を定義しています。
例えば、ブール値では true と false のいずれかであり、数では O か数に対する S のいずれか、リストであれば nil か数とリストに対する cons のいずれかです。
さらに言えば、帰納的に定義された集合の要素になるのは、宣言した構成子を互いに適用したものだけです。
このことがそのまま帰納的に定義された集合に関する推論の方法になります。
すなわち、数は O であるか、より小さい数に S を適用したものであるかのいずれかです。
リストは nil であるか、何らかの数とより小さいリストに cons を適用したものです。
他のものも同様です。
ですから、あるリスト l に関する命題 P があり、 P がすべてのリストに対して成り立つことを示したい場合には、次のように推論します。
大きなリストはそれより小さなリストからのみ作り出され、少しずつ nil に近付いて行きます。
よって、このふたつの言明からすべてのリスト l に関して P が真であることが言えます。
具体的な例で説明しましょう。
- まず、 l が nil のとき P が l について成り立つことを示す。
- それから、 l が cons n l' であるとき、ある数 n とより小さいリスト l' に対して、 P が l' について成り立つと仮定すれば P が l についても成り立つことを示す。
Theorem app_assoc : forall l1 l2 l3 : natlist,
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3).
Proof.
intros l1 l2 l3. induction l1 as [| n l1' IHl1'].
-
reflexivity.
-
simpl. rewrite -> IHl1'. reflexivity. Qed.
自然数上の帰納法と同様に、inductionタクティックでのas ...節はconsの場合の残りのリストにl1'と名前を付けるために使われています。
蒸し返すようですが、この Coq の証明はこうして単に静的なテキストとして読んでいる限り、さほど明白で分かりやすいものではありません。
Coq の証明は、 Coq を対話的に動かしながらポイントごとに「現在のゴールは何か」「コンテキストに何が出ているか」を見て、証明が今どうなっているかを読み下していくことで理解されるようになっています。
しかし、このような証明の途中経過は、全てが証明結果として書き出されるわけではありません。
だからこそ、人間向けの自然言語での証明には証明の筋道がわかるように証明の指針を書いておく必要があるのです。
特に、読者が流れを見失わないよう、ふたつめの場合分けで使う帰納法の仮定が何だったのかわかるようにしておくのは有益なはずです。
比較するために、この定理の非形式的証明を以下に載せます。
定理: 任意のリスト l1、 l2、 l3 について、
(l1 ++ l2) ++ l3 = l1 ++ (l2 ++ l3)
が成り立つ。
証明: l1 についての帰納法で証明する
- まず、 l1 = [] と仮定して
([] ++ l2) ++ l3 = [] ++ (l2 ++ l3) - 次に l1 = n::l1' かつ
(l1' ++ l2) ++ l3 = l1' ++ (l2 ++ l3)
((n :: l1') ++ l2) ++ l3 = (n :: l1') ++ (l2 ++ l3)
n :: ((l1' ++ l2) ++ l3) = n :: (l1' ++ (l2 ++ l3))
Fixpoint rev (l:natlist) : natlist :=
match l with
| nil => nil
| h :: t => rev t ++ [h]
end.
Example test_rev1: rev [1;2;3] = [3;2;1].
Proof. reflexivity. Qed.
Example test_rev2: rev nil = nil.
Proof. reflexivity. Qed.
rev の性質
ここまで見てきたものよりも難易度の高いものですが、リストを反転しても長さの変わらないことを証明します。
下の方法では、ふたつめの場合分けで行き詰まってしまいます。
Theorem rev_length_firsttry : forall l : natlist,
length (rev l) = length l.
Proof.
intros l. induction l as [| n l' IHl'].
-
reflexivity.
-
simpl.
rewrite <- IHl'.
Abort.
++ と length に関する等式が成り立つことを示せれば証明が先に進むはずです。
この等式を別個の補題として記述してみましょう。
Theorem app_length : forall l1 l2 : natlist,
length (l1 ++ l2) = (length l1) + (length l2).
Proof.
intros l1 l2. induction l1 as [| n l1' IHl1'].
-
reflexivity.
-
simpl. rewrite -> IHl1'. reflexivity. Qed.
補題をできる限り一般的なものにするため、revの定義で出てくるような結果に対してではなく、「全ての」natlistに対するものとして形式化しました。
目的としたゴールは明らかにリストの反転という状態に全く依存していないという点で、この一般化は自然なものでしょう。
さらに、この一般的な性質の方がより簡単に証明できます。
これで、元々の証明ができるようになりました。
Theorem rev_length : forall l : natlist,
length (rev l) = length l.
Proof.
intros l. induction l as [| n l' IHl'].
-
reflexivity.
-
simpl. rewrite -> app_length, plus_comm.
simpl. rewrite -> IHl'. reflexivity. Qed.
対比として、この二つの定理の非形式的な証明を見てみましょう
定理: 二つのリスト l1 と l2 について、 length (l1 ++ l2) = length l1 + length l2 が成り立つ。
証明: l1 についての帰納法で証明する。
定理: 任意のリスト l について length (rev l) = length l が成り立つ。
証明: l についての帰納法で証明する。
- まず、 l = [] と仮定して
length (rev []) = length [] - 次に、 l = n::l' かつ
length (rev l') = length l'
length (rev (n :: l')) = length (n :: l')
length ((rev l') ++ [n]) = S (length l')
length (rev l') + length [n] = S (length l')
こういった証明のスタイルは、長ったらしく杓子定規な感じがします。
最初の何回かは別にして、それ以後は、細かいところは省略してしまって(必要なら頭の中や紙の上で追うのは簡単ですから)、自明でないところにだけ注目した方がわかりやすいでしょう。
そのように省略がちに書けば、上の証明は次のようになります。
定理:
任意のリスト l について length (rev l) = length l が成り立つ。
証明: まず、任意の l について length (l ++ [n]) = S (length l) であることに注目する(これは l についての帰納法から自明である)。
もとの性質については、 l についての帰納法から示し、 l = n'::l' の場合については、上の性質と帰納法の仮定から導かれる。 ☐
どちらのスタイルの方が好ましいかは、読み手の証明への馴れや、彼らが今まで触れてきた証明がどちらに近いかに依ります。
本書の目的としては冗長なスタイルの方が無難でしょう。
これまで見てきたように、定理を証明するには既に証明した定理を、例えばrewriteを通じて、使うことができます。
しかし、定理を使うためにはその名前を知らなければなりません!
使えそうな定理の名前をすべて覚えておくのはとても大変です。
今まで証明した定理を覚えておくだけでも大変なのに、その名前となったら尚更です。
Coq の Search コマンドはこのような場合にとても便利です。
スクリプトファイルに Search foo と書いて評価させると、 foo に関する証明がすべて表示されます。
例えば、次の部分のコメントを外せば、これまで rev に関して証明した定理が表示されます。
続く練習問題に取り組む際や読み進める際には、常に Search コマンドのことを頭の隅に置いておくといいでしょう。
そうすることでずいぶん時間の節約ができるはずです。
もし ProofGeneral を使っているのなら、 C-c C-a C-a とキー入力をすることで Search コマンドを使うことができます。
その結果をエディタに貼り付けるには C-c C-; を使うことができます。
Theorem app_nil_r : forall l : natlist,
l ++ [] = l.
Proof.
Admitted.
Theorem rev_app_distr: forall l1 l2 : natlist,
rev (l1 ++ l2) = rev l2 ++ rev l1.
Proof.
Admitted.
Theorem rev_involutive : forall l : natlist,
rev (rev l) = l.
Proof.
Admitted.
次の問題には簡単な解法があります。
こんがらがってしまったようであれば、少し戻って単純な方法を探してみましょう。
Theorem app_assoc4 : forall l1 l2 l3 l4 : natlist,
l1 ++ (l2 ++ (l3 ++ l4)) = ((l1 ++ l2) ++ l3) ++ l4.
Proof.
Admitted.
前に書いた nonzeros 関数に関する練習問題です。
Lemma nonzeros_app : forall l1 l2 : natlist,
nonzeros (l1 ++ l2) = (nonzeros l1) ++ (nonzeros l2).
Proof.
Admitted.
☐
練習問題: ★★, standard (eqblist)
Fixpoint eqblist (l1 l2 : natlist) : bool
. Admitted.
Example test_eqblist1 :
(eqblist nil nil = true).
Admitted.
Example test_eqblist2 :
eqblist [1;2;3] [1;2;3] = true.
Admitted.
Example test_eqblist3 :
eqblist [1;2;3] [1;2;4] = false.
Admitted.
Theorem eqblist_refl : forall l:natlist,
true = eqblist l l.
Proof.
Admitted.
☐
上で見たバッグについて、いくつかの定理を証明します。
☐
以下の leb に関する補題は、この次の課題に使えるかもしれません。
Theorem leb_n_Sn : forall n,
n <=? (S n) = true.
Proof.
intros n. induction n as [| n' IHn'].
-
simpl. reflexivity.
-
simpl. rewrite IHn'. reflexivity. Qed.
次の練習問題を解く前に、remove_one の定義ができあがっていることを確認してください。
練習問題: ★★★, advanced (remove_does_not_increase_count)
Theorem remove_does_not_increase_count: forall (s : bag),
(count 0 (remove_one 0 s)) <=? (count 0 s) = true.
Proof.
Admitted.
(count 0 (remove_one 0 s)) <=? (count 0 s) = true.
Proof.
Admitted.
☐
練習問題: ★★★, optional (bag_count_sum)
練習問題: ★★★★, advanced (rev_injective)
forall (l1 l2 : natlist), rev l1 = rev l2 -> l1 = l2
☐
Fixpoint nth_bad (l:natlist) (n:nat) : nat :=
match l with
| nil => 42
| a :: l' => match n =? O with
| true => a
| false => nth_bad l' (pred n)
end
end.
この方法はあまり良くありません。
もしnth_badが42を返したとしても、別の処理なしにはそれが実際にリストにあった値なのかを判断できません。
より良い方法としては、nth_badの返し値の型を変更して、エラー値を扱えるようにするというものです。
この型をnatoptionと呼ぶことにします。
We can then change the above definition of nth_bad to
return None when the list is too short and Some a when the
list has enough members and a appears at position n. We call
this new function nth_error to indicate that it may result in an
error.
Fixpoint nth_error (l:natlist) (n:nat) : natoption :=
match l with
| nil => None
| a :: l' => match n =? O with
| true => Some a
| false => nth_error l' (pred n)
end
end.
Example test_nth_error1 : nth_error [4;5;6;7] 0 = Some 4.
Proof. reflexivity. Qed.
Example test_nth_error2 : nth_error [4;5;6;7] 3 = Some 7.
Proof. reflexivity. Qed.
Example test_nth_error3 : nth_error [4;5;6;7] 9 = None.
Proof. reflexivity. Qed.
(HTML版では上の例での証明部分の常套句が消えています。
もし見たければ直後の四角をクリックしてください。)
この機会に、 Coq のプログラミング言語としての機能として、条件式を紹介しておきましょう。
Fixpoint nth_error' (l:natlist) (n:nat) : natoption :=
match l with
| nil => None
| a :: l' => if n =? O then Some a
else nth_error' l' (pred n)
end.
Coq の条件式(if式)は他の言語に見られるものとほとんど同じですが、少しだけ一般化されています。
Coq には 組み込みのブーリアン型がないため、 Coq の条件式では、実際には、構成子のふたつある任意の帰納型に対する分岐ができます。
条件部の式が Inductive の定義の最初の構成子に評価された場合には真、ふたつめの構成子に評価された場合には偽と見做されます。
Definition option_elim (d : nat) (o : natoption) : nat :=
match o with
| Some n' => n'
| None => d
end.
Definition hd_error (l : natlist) : natoption
. Admitted.
Example test_hd_error1 : hd_error [] = None.
Admitted.
Example test_hd_error2 : hd_error [1] = Some 1.
Admitted.
Example test_hd_error3 : hd_error [5;6] = Some 5.
Admitted.
☐
This exercise relates your new hd_error to the old hd.
Exercise: 1 star, standard, optional (option_elim_hd)
Theorem option_elim_hd : forall (l:natlist) (default:nat),
hd default l = option_elim default (hd_error l).
Proof.
Admitted.
☐
Partial Maps
Internally, an id is just a number. Introducing a separate type
by wrapping each nat with the tag Id makes definitions more
readable and gives us the flexibility to change representations
later if we wish.
We'll also need an equality test for ids:
☐
Now we define the type of partial maps:
Module PartialMap.
Export NatList.
Inductive partial_map : Type :=
| empty
| record (i : id) (v : nat) (m : partial_map).
この宣言は次のように読めます。
「partial_map を構成する方法はふたつある。
構成子 empty で空の部分写像を表現するか、構成子 record をキーと値と既存の partial_map に適用してキーと値の対応を追加した partial_map を構成するかのいずれかである。」
The update function overrides the entry for a given key in a
partial map by shadowing it with a new one (or simply adds a new
entry if the given key is not already present).
最後に、 find 関数は、 partial_map から与えられたキーに対応する値を探し出すものです。
キーが見つからなかった場合には None を返し、キーが val に結び付けられていた場合には Some val を返します。
同じキーが複数の値に結び付けられている場合には、最初に見つけた値を返します。
Fixpoint find (x : id) (d : partial_map) : natoption :=
match d with
| empty => None
| record y v d' => if eqb_id x y
then Some v
else find x d'
end.
Theorem update_eq :
forall (d : partial_map) (x : id) (v: nat),
find x (update d x v) = Some v.
Proof.
Admitted.
forall (d : partial_map) (x : id) (v: nat),
find x (update d x v) = Some v.
Proof.
Admitted.
Theorem update_neq :
forall (d : partial_map) (x y : id) (o: nat),
eqb_id x y = false -> find x (update d y o) = find x d.
Proof.
Admitted.
forall (d : partial_map) (x y : id) (o: nat),
eqb_id x y = false -> find x (update d y o) = find x d.
Proof.
Admitted.
☐
How many elements does the type baz have? (Explain in words,
in a comment.)
☐