eqlには3種類の意味がある
表題の通り、Common Lispでは下記の意味があります。
- 関数
eql
- 型
eql
eql-specializer
なんでこんなにあるんでしょうね。
兄弟分である、eq
、equal
、equalp
さんたちは1つの意味しかありませんよ?
それぞれ説明していきます。
関数eql
普通はこいつです。
2つのオブジェクトが等しいかどうかを調べるときに使います。
関数eq
より適当ではないものの、関数equal
みたいに
詳しく調査して欲しくないという位置づけだと思います。
厳密に定義されており、下記のどれかが当てはまっているならt
です。
eq
がt
の場合。- どちらも
number
であり、同じ型で同じ値の場合 - どちらも
character
であり、同じ文字の場合
1つめのeq
はそのままアドレスを比較します。
2つめは、同じ型が要求されているので、数値として等しいだけの場合はダメ。
* (eql 0.0 0) →nil * (eql 0.0 0.0) →t * (eql 0.0d0 0.0s0) →nil * (eql 0.0d0 0.0d0) →t
3つめはそのままです。
* (eql #\a #\a) →t * (eql #\a #\A) →nil
型eql
Type-specifier
と呼ばれるものであり、
型という中で、値がeql
かどうかを調査します。
例えば次の通り。
* (typep 10 '(eql 10)) →t * (typep (cons nil nil) `(eql ,(cons nil nil))) →nil
余談ですが、型を評価する際に、内部で別の形式に変形することが良くあります。
例えば
(or (integer 10 200) (integer 100 300))
を
(integer 10 300)
のように変えたりします。
型eql
の場合も
(eql 10)
を
(integer 10 10)
に変形することがあります。
だから何だってわけじゃないんですが。
eql-specializer
これはmethod
を定義するときに使うものです。
method
は、引数に値の特性を指定することができます。
例えば下記の通り。
(defmethod aaa ((a integer) (b string)) ...)
この場合は、第一引数a
にinteger
を、第二引数b
にstring
を指定しています。
注意して欲しいのは、このinteger
やstring
と記述しているのは、
「型」ではなく「class
およびeql-specializer
」です。
下記の例の引数は、型(eql 10)
ではなく、eql-specializer
の指定です。
(defmethod bbb ((a (eql 10))) ...)
何が違うんだとしか思えないでしょう。
integer
と書けて、(eql 10)
と書けるなら、型じゃないのか?
でも型じゃないんです。
型が指定できないので
(defmethod bbb ((a (satisfies oddp))) ...)
や
(defmethod bbb ((a (not (eql 10)))) ...)
はエラーです。
こいつもエラーなので注意。
(defmethod bbb ((a (real 10 20))) ...)
じゃあinteger
とstring
は型じゃないのか?
と思われるかもしれませんが、型じゃありません。
型のふりをしたclos
オブジェクトです。
clos
の世界とlisp
の世界をある程度似せるために、
symbol
だけで表記された標準の型については、
同じclass
が用意されている場合があります。
何が用意されているのかは処理系依存ですが、
find-class
関数を使うことで調査できます。
clisp
の例を示します。
(find-class 'integer nil) →#<BUILT-IN-CLASS INTEGER> (find-class 'atom nil) →nil
なお、eql-specializer
の引数は、
defmethod
宣言時に一度だけ評価されます。
method
が呼び出されるたびに評価されるわけではありません。
例を示します。
呼び出すたびに値が増えていく関数hello
を作成します。
(let ((value 0)) (defun hello () (incf value)))
generic-function
のaaa
を作成します。
(defgeneric aaa (arg))
関数hello
をeql-specializer
の引数として、method
を定義します。
;; ここで(hello)は1を返却 (defmethod aaa ((arg (eql (hello)))) :hello) →#<STANDARD-METHOD ((EQL 1))>
実行してみます。
;; eql-specializerが機能している (aaa 1) →:HELLO ;; 何度か実行する (aaa 1) →:HELLO (aaa 1) →:HELLO ;; 確認 (hello) →2 ;; エラー (aaa 2) →no-applicable-method