Common Lispにも、他の言語と同様に、2つのオブジェクトが「同じ」かどうかを判断するためのルールがあります。 このルールは4つのレベルを定義していて、それぞれのレベルには、そのレベルでのチェックを行う関数があります。 レベルは、最も厳しいものから最も緩いものへと並んでいます。
eq最初のレベルは、オブジェクトの同一性です。
この等価性は、関数eqでチェックします。
等しいかどうかを調べる2つのオブジェクトは、まったく同じオブジェクトでなければなりません。
(eq 'apples 'apples) ; => T
(eq 'apples 'oranges) ; => NIL
(eq '(a b c) '(a b c) ; => NIL (these two lists have the same contents but are not the same list)
(let ((list1 '(a b c)) (list2 list1))
(eq list1 list2)) ; => T (these two lists are the same list)
eql2番目のレベルでは、数値と文字の等価性が加わります。
この等価性は、関数eqlでチェックします。
チェックの方法は、引数の型によって変わります。
eqである2つのオブジェクトは、eqlですeqlですeqlです(eql 1 1) ; => T
(eql 1 1/1) ; => NIL (one number is an integer, the other a rational)
(eql #\c #\c) ; => T
(eql #\c #\C) ; => NIL (case is different)
なぜ数値と文字が、eqによるオブジェクトの同一性で比較されないのか、不思議に思うかもしれません。
Common Lispの標準では、実装が望むなら数値や文字をコピーすることが許されています。
そのため、0と0はeqでないことがあります。それぞれが数値0の異なるインスタンスである可能性があるからです。
equal3番目のレベルでは、構造的な類似性をチェックします。
この等価性は、equalでチェックします。
チェックの方法は、引数の型によって変わります。
eqと同様に比較されますeqlと同様に比較されますequalであればequalです。
これは再帰的に行われます。eqlであればequalですeqと同様に比較されますequalです。
(ここには、パス名の構成要素となる文字列の大文字小文字の扱いに関して、実装依存の動作が入り込む余地があります。)eqと同様に比較されます(equal '(a (b c)) '(a (b c))) ; => T (conses are equal if their contents are equal)
(equal "hello" "hello") ; => T
(equal "hello" "HELLO") ; => NIL
(equal #(1 2 3) #(1 2 3)) ; => NIL (arrays are equal only if eq)
(equal #P"foo/bar.md" #P"foo/bar.md") ; => T (pathnames are equal if "functionally equivalent"
equalp4番目で最も緩いレベルの等価性は、equalpでチェックします。
チェックの方法は、型によって変わります。
equalpであれば、それらはequalpですequalpですequalpであればequalpです。
これは再帰的に行われます。equalpであればequalpです。equalpであればequalpです。:test関数を持ち、同じキーを持ち(その:test関数で比較した場合)、それらのキーがequalpで比較して同じ値を持てばequalpです。(equalp 1 1.0) ; => T
(equalp #\c #\C) ; => T
(equalp "hello" "HELLO") ; => T
(equalp #(1 2 3) #(1.0 2.0 3.0)) ; => T (arrays contain elements which are `equalp`)
(equal #S(TEST :SLOT1 'a :SLOT2 'b)
#S(TEST :SLOT1 'a :SLOT2 'b)) ; => T (structures of the same class with slots that have values which are `equalp`)
ここまでに挙げたのは、「汎用的な」等価性の関数です。
これらは、定義どおり、どのような型に対しても機能します。
これは、比較するオブジェクトの型が実行時までわからないような汎用的なコードを書くときに役立ちます。
ただし、比較する型がわかっている場合には、型に特化した等価性の関数を使うほうが「よりよいスタイル」だと一般に考えられています。
たとえば、equalではなくstring=を使います。
これらの関数は、関連するコンセプトの中で紹介し、説明します。