いろいろな「等しさ」

さて、8章2節。
真偽値を返す関数のことを「述語」と呼び、とくに等しいかどうかを調べる関数を「等価述語」と呼ぶ。
C++STLや.NET Fremeworkでも出てくる用語なので、一応知ってた。
英語でいうとpredicateね。


...はい。で、本題。
「値が等しい」と一口に言っても、「等しさ」にはいろんな意味がありますよ、と。
以下、いくつかの等価述語を挙げる。最後のlset=を除き、いずれも引数は2個。

equal?

おそらく一番直感的な「等しさ」を調べる関数。

引数が複合データ型(リスト、文字列など)の場合

要素を先頭から比較していって、全ての要素が等しければ#tとなる。
ネストしたリストの場合は、再帰的に調べる。

引数が単純なデータ型(真偽値、数値、文字、シンボルなど)の場合

原則として、両者の型と値が同じならば#tとなる。
数値の場合は「正確数」「不正確数」も考慮する。
不正確数とは浮動小数点など、誤差を含む数値のことらしい。

eq?

C++C#でいえば、インスタンスが同じ場合に#tとなる。
アドレスを比べるので等価述語の中では最速。
consは言わばペアを生成するコンストラクタなので、

gosh> (eq? (cons 1 2) (cons 1 2))
#f

となる。

gosh> (define p (cons 1 2))
p
gosh> (eq? p p)
#t

はpが同じインスタンスを示すので#t。


単純なデータ型では、真偽値とシンボルのみ、eq?で確実に比較できることが保証されている。

eqv?

equal?とeq?の折衷的な述語。
前述のとおり、同じ文字同士や同じ数値同士でもeq?が#tになるとは限らないので、複合データ型の場合はアドレスで調べ(eq?の動作)、単純データ型の場合は値で調べる(equal?の動作)。

=

正確数と不正確数の等価性も比べられる数値専用の等価述語。

gosh> (= 1 1.0)
#t

となる。doubleとintの場合も調べられますよ、みたいな?

char=?

同じ文字がどうか調べる。大文字と小文字は区別する。

gosh> (char=? #\a #\a)
#t
gosh> (char=? #\a #\A)
#f

char-ci=?

同じ文字がどうか調べる。大文字と小文字を区別しない。

string=?

同じ文字列かどうか調べる。大文字と小文字を区別する。

gosh> (string=? "scheme" "scheme")
#t
gosh> (string=? "scheme" "Scheme")
#f

string-ci=?

同じ文字列かどうか調べる。大文字と小文字を区別しない。

isomorphic?

equal?が#tで、かつ、リストをグラフとして見たときにグラフの形が同じならば#tとなる。

gosh> (use util.isomorph)
#<undef>
gosh> (define p (cons 1 2))
p
gosh> (define a (list p p))
a
gosh> (isomorphic? a (list p p))
#t
gosh> (isomorphic? a (list (cons 1 2) (cons 1 2)))
#f

正直、当分使いそうにない予感がする。

lset=?

こいつは3引数(なのかな?)。
リストを集合とみなして、同じ集合かどうかを調べる。
つまり、順序は関係なくて同じ要素を含んでれば#tとなる。
その、「同じ要素」かどうかを調べるための等価述語を1個めの引数に取る。

gosh> (use srfi-1)
#<undef>
gosh> (lset= eqv? '(1 2 3) '(3 1 2))
#t

lset=?じゃなくてlset=なのか。間違えそうだなー。