以下の環境でSLIBを使っていきます。
CPU : Intel Core i5 2.60GHz
OS : WIndows 10
言語 : Gauche 0.9.9
準備
今回のテーマは集合です。
SLIBでは集合に対する操作をリストに対して使えるので、リストを集合とみなして使っていけるようです。
まずはモジュールのインポートから。
gosh> (use slib) gosh> (require 'common-list-functions) #t
定義
次に簡単な集合とを定義します。
gosh> (define A '(0 2 5)) A gosh> (define B '(1 2 8 9)) B
基本操作
adjoin
adjoinは集合に要素を加える操作となります。試しに集合Aに要素1を加えてみましょう。
gosh> (adjoin 1 A) (1 0 2 5) gosh> A (0 2 5)
集合の要素が1つ増えているのがわかります。ただ、もとの集合Aは変化していないので注意が必要です。
当然ですが、以下のように、すでに集合に含まれている要素をadjoinしてもなにも変わりません。
gosh> (adjoin 0 A) (0 2 5)
union
unionは和集合の操作を実行します。
gosh> (union A B) (0 5 1 2 8 9)
intersection
intersectionは積集合の操作を実行します。
gosh> (intersection A B) (2)
subset?
subset?はが真か偽かを判断します。
gosh> (subset? A B) #f gosh> (subset? '(0 2) A) #t gosh> (subset? '(0 2) B) #f
要素0と要素2はともに集合Aに含まれるのではは真になります。
member-if
説明がまどろっこしいのでいきなり例を見てみます。
(member-if odd? '(2 4 6 9 5 8 7)) (9 5 8 7)
odd?というのは与えられた数字が奇数かどうでないかを判定する関数です。この場合、member-ifが行うのはリストを先頭から調べ、一番初めに奇数という条件を満たした要素と後ろに残った要素からなるリストを返すことです。
ですから、条件を満たす要素がすでにリストの先頭にある場合は、なにも変わらないように見えます。例えば先ほどと同じリストに対し、偶数か否かを判定するeven?を使えば次のようになります。
gosh> (member-if even? '(2 4 6 9 5 8 7)) (2 4 6 9 5 8 7)
もう少し高度な操作
ほかにもいろいろできそうなので、下準備として以下の関数を定義します。
gosh> (define (contain-char c) (lambda (pname) (number? (string-scan pname c)))) contain-char
contain-charはstring中に文字cが含まれているかどうかを判定する関数を返します。
使い方は以下の通りです。
gosh> ((contain-char "l") "mike") #f gosh> ((contain-char "m") "mike") #t
mikeという文字列は文字lを含みませんが文字mは含んでいる、というわけですね。
some
まずは人名のリストを作ります。
gosh> (define name-list '("haruhi" "kyon" "yuki" "mikuru" "itsuki")) name-list
someは与えられたリストに条件を満たす要素が1つでもあれば真となります。
アルファベットiを使う名前の人がいるか調べてみます。
gosh> (some (contain-char "i") name-list) #t
#tなので条件を満たす人が少なくとも1人はいるようです。要素5の集合なので見れば一目瞭然ですが。
数式でイメージするとこんな感じでしょうか↓
every
everyはすべての要素が条件を満たす場合に真となります。
gosh> (every (contain-char "i") name-list) #f
人名リストname-listのうち、人名kyonはアルファベットiを含まないので結果は偽となります。
everyは数式だとこんな感じでしょうか↓
notany
notanyはいずれの要素も条件を満たさない場合に真となります。
例を以下に示します。
gosh> (notany (contain-char "i") name-list) #f gosh> (notany (contain-char "p") name-list) #t
人名リストにはアルファベットiを含む人名がいくつかあるので偽となります。また、アルファベットpを含む人名はさすがにいないので真となります。
数式だとこんな感じでしょうか↓
notevery
最後にnoteveryです。
こちらはnotanyのときよりさらにわかりやすいと思います。
名前の通りeveryの否定です。1つでも条件を満たさない要素があれば真となります。
例を見てみましょう。
gosh> (notevery (contain-char "i") name-list) #t
人名リストにある名前はアルファベットiを含まない名前kyonがあります。したがって真となります。
数式だとこんな感じですね↓