OCaml一人勉強会 - Set

忙しいからって二週間も放置していいのかと自問した末に進めることにしました。なんだそれ。
Web 上に全ての関数に関して詳しく書いてあるページが見つけられなかったので網羅的に調べてみました…
さて Set は汎用の集合ライブラリです。様々な型で使用できるようファンクタが使われています。

# module StrSet = Set.Make(String);;
module StrSet :
  sig
    type elt = String.t
    type t = Set.Make(String).t
    val empty : t
    val is_empty : t -> bool
    ...

Set.Make がファンクタです。そのモジュールの型を表す t と比較関数 compare を持つモジュールなら何でも使用することが出来ます。というのはつまり使用したい場合は t と compare 定義してね、ということですね。
早速使ってみましょう。まずは空の集合から。

# open StrSet;;
# let s = empty;;
val s : StrSet.t = <abstr>
# cardinal s;;
- : int = 0

empty は空の集合を表しています。 cardinal で要素数を見ても0ですね。
空ではつまらないので要素を追加してみましょう。

# let s = add "hoge" s;;
val s : StrSet.t = <abstr>
# is_empty s;;
- : bool = false

add は集合に要素を追加します。is_empty によって要素が空かどうかを知ることが出来ます。
要素についてもっと知りたい時は elements によって要素のリストを得ることも出来ます。

# elements s;;
- : StrSet.elt list = ["hoge"]

要素を一つだけ持つ集合なら singleton 関数で作ることも出来ます。まぎわらしーかんすーめーですね ^^;;;
singleton で作った集合が上の集合と等しいか equal で調べてみましょう。

# equal s (singleton "hoge");;
- : bool = true
# compare s (singleton "hoge");;
- : int = 0

compare 関数によって集合同士を比較することも出来ます。要素が均しい時 0 を、そうでない時は多分要素数とか適当に見て 1 か -1 を返す感じです。まあ正直よくわからないので詳細は実装を…
ある要素を集合から取り除きたい時は remove を使います。

# let s = remove "hoge" s;;
val s : StrSet.t = <abstr>

ちゃんと取り除かれているか elements で確かめてもいいのですが、ここでは要素が集合に含まれているかどうかを調べる mem を使いましょう。

# mem "hoge" s;;
- : bool = false

確かに取り除かれています。 add とか remove とか基本的に副作用がないので、手続き脳の人はちょっとやりづらいと感じるかも知れません。ボクは感じました。
集合と集合の和は union によって作ることができます。

# let s0 = add "hige" (add "hoge" empty);;
val s0 : StrSet.t = <abstr>
# let s1 = add "huga" (add "hoge" empty);;
val s1 : StrSet.t = <abstr>
# elements (union s0 s1);;
- : StrSet.elt list = ["hige"; "hoge"; "huga"]

集合と集合の積は inter によって作ることができます。また、集合と集合の差は diff によって作ることができます。

# elements (inter s0 s1);;
- : StrSet.elt list = ["hoge"]
# elements (diff s0 s1);;
- : StrSet.elt list = ["hige"]
# elements (diff s1 s0);;
- : StrSet.elt list = ["huga"]

diff した集合は決して ["hoge"; "huga"] にはならないことに注意が必要です。引数の順序ややこしいですね。差というよりは引き算のほうが直感的かもしれません。まあ普通に対称差ではなく差集合ということですね。
ある集合がある集合に対して部分集合かどうかを知るためには subset を使います。

# subset (singleton "hige") s0;;
- : bool = true

第二引数の集合に対して第一引数の集合が部分集合かを返します。どう考えても引数の順序間違えそうです。何か第二引数の方が基本的に偉い感じなのは段々分かってきました。
さて Set には集合の全要素に対して何らかの処理を行いたい時のイテレータが複数あります。
まずは普通に全要素に関数を適用する iter

# iter (fun s -> print_endline s) s0;;
hige
hoge
- : unit = ()

畳み込みのための fold もあります。畳み込みとか偉そうに言ってますが手続き脳なんで何も分かってませんごめんなさいごめんなさい。

# fold (fun s t -> t ^ "|" ^ s) s0 "huga";;
- : string = "huga|hige|hoge"

全ての要素がある条件を満たすか判定する for_all もあります。文字列集合に3文字以下の文字列が存在しないかを調べるためにはこんな感じ。

# for_all (fun s -> 3 < String.length s) s0;;
- : bool = true

exists である条件を満たす要素が存在するかを判定することもできます。

# exists (fun s -> 3 == String.length s) s0;;
- : bool = false

ある集合のうち条件を満たす要素からなる集合を返す filter とかそのまんまなものもあります。

# elements (filter (fun s -> s = "hoge") s0);;
- : StrSet.elt list = ["hoge"]

さらに条件を満たすものからなる集合と、満たさないものからなる集合のタプルを返す partition も。

# let (t,f) = partition (fun s -> s = "hoge") s0;;
val t : StrSet.t = <abstr>
val f : StrSet.t = <abstr>
# elements t;;
- : StrSet.elt list = ["hoge"]
# elements f;;
- : StrSet.elt list = ["hige"]

どうでもいい感じなのであとは適当に。最も小さい要素を返す min_elt 、最も大きい要素を返す max_elt 、取りあえず一つ要素を返す choose 、何かよくわからないけど集合を分割する split。
choose は空の集合を渡すと例外が発生します。

# choose empty;;
Exception: Not_found.

split は正直よく分かりません。第一引数に要素、第二引数に集合を受け取り、集合に要素が含まれていれば適当に集合を分割し、集合と true と集合のタプルを返します。集合に要素が含まれていなければ、空の集合と false と元の集合のタプルを返します。謎です。


とりあえず一つずつ見ていきましたが、まあ一通りは揃っているなあという感じですね。軽くではあるものの、さすがに全部試したら長くなったので Map はまた今度。
ところで、いい加減
[rakuten:book:12062857:detail]
とか買おうかと思いつつもお金がないので買えないし昼ごはんが食べられません。悲しいことですね。