2012-03-03 18 views
3

どのようにして、特定のクラスのサブクラスを指定するシンボルを取るジェネリック関数を特化できますか? 例:与えられたクラスのサブクラスのジェネリック関数を特化する方法

(defclass a()()) 
(defclass b (a)()) 
(defclass c (b)()) 
(defclass d()()) 

(defgeneric fun (param)) 
(defmethod fun ((param (<subclass of> a))) 
    (format t "~a is a subclass of A~%" param)) 

(fun 'c) ;-> "C is a subclass of A" 
(fun 'd) ;-> Error: not found method for generic function call (fun 'd) 

CLOSでこのようなディスパッチが可能ですか?それが「サブクラス」の代わりに何を書き込むべきですか?

答えて

1

代替、醜い、バージョンは値がハードコードになります。

私が続行する前に、用語に関する簡単な注釈が重要だと思います。このように

Common LispのHyperSpec用語集defines "サブクラス":

別のクラスから継承するクラスは、スーパークラスと呼ばれます。 (NO クラスは、自身のサブクラスではありません。)私は、「適切なサブクラス」の定義であることを期待するよう

この定義は、直感的ながら、私には奇妙に思えます。しかし、すべてのクラスがdefines「サブタイプ」タイプであり、それのように:

その会員と同じまたは別のタイプの会員の適切なサブセットであるタイプ、スーパータイプと呼ばれます。 (すべてのタイプはそれ自身のサブタイプです)

「すべてのタイプはそれ自身のサブタイプです。「

それもdefines 『適切なサブタイプ』:

(タイプの)タイプ(すなわち、同じタイプではないタイプのサブタイプで、その要素は、 ``適切なサブセットであります'タイプの)。

したがって、あなたの例では、B及びCは、Aのサブクラスであり、またサブタイプ一方のBで、C、はA.

のサブタイプであります

デートに置かれるものfmethodは"parameter specializer name"です。シンボル、クラス(入力するのは少し難しい)、またはeqlで始まるリストにすることができます。シンボルを指定すると、そのシンボルで指定されたクラスが指定されます(これはもちろんタイプです)。 eqlリストは、リスト内のものにeqlであるオブジェクトで構成されるタイプを指定します。

このメソッドは、スペシャライザが指定する型のメンバであるオブジェクトと一致します。もちろん、XのサブタイプのメンバーもXのメンバーです。

最初の問題は、シンボルオブジェクトをメソッドに渡していることです。すべてのシンボルのタイプはSYMBOLです。クラスに名前をつけるシンボルは、この点で違いはありません。クラスとの唯一の関係はクラスの名前であり、サブタイプの関係ではありません。

クラスオブジェクト(find-classが返されます)がありますが、クラスオブジェクトの型は通常、そのサブクラスのクラスオブジェクトの型と同じであるため、メソッドの特殊化のシンボルよりも優れていません。

したがって、インスタンスを使用するか、AMOPと読むことで、独自のタイプの汎用関数を作成する方法を学ぶことができます。

インスタンスを持っていたら、このような方法で記述することができます。

(defmethod fun ((param symbol)) 
    (fun (retrieve-instance param))) 
:あなたは、クラスのインスタンスを取得する簡単な方法を持っている場合、あなたはこのラッパーを書くことができ

(defmethod fun ((param a)) 
    (if (eq (type-of param) 'a) 
    (call-next-method) 
    (format t "~a is a subclass of A~%" (type-of param)))) 

それから、シンボルを楽しいものに渡して、あなたが望む結果を得ることができます。

あなたは(、標準で指定されたが、広く利用されていなかったCloser Projectを参照)AMOP機能を使用する場合は、このようなretrieve-instance定義することができます:メソッドディスパッチは単なるについてです

(defun retrieve-instance (name) 
    (let ((class (find-class name))) 
    (unless (class-finalized-p class) 
     (finalize-inheritance class)) 
    (class-prototype class))) 

注ことをclass-prototypeの結果だけが役に立ちます。それを変更しようとしないでください。

4

Common Lispの関数SUBTYPEPを持っていることに注意してください:

CL-USER 15 > (subtypep 'd 'a) 
NIL 
T 

CL-USER 16 > (subtypep 'c 'a) 
T 
T 

(それがサブタイプである場合には、第1と言う)2つの戻り値の意味についてSUBTYPEPのドキュメントを参照してください。クラスもタイプです。あなたの機能があることを意味

ちょうどこの:

(defun fun (class-name) 
    (if (subtypep class-name 'a) 
     (format t "~a is a subclass of A~%" class-name) 
    (error "wtf"))) 

注意:この方法では、継承は、クラスの継承上で動作します。

(defmethod fun ((param a)) 
    (format t "~a is a subclass of A~%" (class-name (class-of param)))) 

以上がクラスAのインスタンスを取ります。それはあなたが特定のクラスのインスタンスを渡す必要が継承を使用することを意味します。

コールは:

CL-USER 29 > (fun (make-instance 'a)) 
A is a subclass of A 
NIL 

CL-USER 30 > (fun (make-instance 'c)) 
C is a subclass of A 
NIL 

CL-USER 31 > (fun (make-instance 'd)) 

Error: No applicable methods for #<STANDARD-GENERIC-FUNCTION FUN 418001813C> 
with args (#<D 40200011E3>) 
    1 (continue) Call #<STANDARD-GENERIC-FUNCTION FUN 418001813C> again 
    2 (abort) Return to level 0. 
    3 Return to top loop level 0. 

Type :b for backtrace or :c <option number> to proceed. 
Type :bug-form "<subject>" for a bug report template or :? for other options. 

CL-USER 32 : 1 > 

簡素化する方法がある^ H^H^H^H^H^H^H^H、それが簡単に呼び出すために作る:あなたはクラスであることを確認することができますCLOS:FINALIZE-INHERITANCEのようなものを使用して完成され、クラスプロトタイプを入力として使用します(CLASS-PROTOTYPEを呼び出します)。そうすれば、ディスパッチするクラスのインスタンスを作成する必要はありません。プロトタイプインスタンスを使用するだけです。あなたは簡単にだけCLOSのディスパッチを使用して、この正確なタスクを実行することはできません

(defmethod fun0 ((param (eql 'b))) 
    T) 

(defmethod fun0 ((param (eql 'c))) 
    T) 
関連する問題