いろいろな「等しさ」
さて、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=なのか。間違えそうだなー。