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でそのようなディスパッチは可能ですか?もしそうなら、「のサブクラス」の代わりに何を書くべきですか?

4

2 に答える 2

5

Common Lisp には次の機能があることに注意してくださいSUBTYPEP

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

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

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))))

上記は class のインスタンスを取ります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)。そうすれば、ディスパッチのためにクラスのインスタンスを作成する必要がなくなります。プロトタイプインスタンスを使用するだけです。

別の醜いバージョンは、値をハードコーディングすることです。

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

(defmethod fun0 ((param (eql 'c)))
  T)
于 2012-03-03T08:02:49.100 に答える
2

CLOSディスパッチのみを使用して、この正確なタスクを簡単に実行することはできません。

先に進む前に、用語について簡単に説明しておくことが重要だと思います。

Common Lisp HyperSpec 用語集では、 「サブクラス」を次のように定義しています。

スーパークラスと呼ばれる、別のクラスから継承するクラス。(どのクラスもそれ自体のサブクラスではありません。)

この定義は直感的ですが、「適切なサブクラス」の定義であると予想されるため、私には奇妙に思えます。ただし、すべてのクラスは型であり、「サブタイプ」を次のように定義します。

メンバーシップが、スーパータイプと呼ばれる別のタイプのメンバーシップと同じか、そのメンバーシップの適切なサブセットであるタイプ。(すべてのタイプはそれ自体のサブタイプです。)

「すべてのタイプはそれ自体のサブタイプです」という括弧に注意してください。

また、「適切なサブタイプ」も定義します。

(型の) 型と同じ型ではない型のサブタイプ (つまり、その要素は型の「適切なサブセット」です)。

したがって、あなたの例では、 B と C は A のサブクラスであり、サブタイプでもあります。一方、B、C、および Aは A のサブタイプです。

defmethod に入れるのは「パラメータ特化名」です。シンボル、クラス (入力が少し難しい)、または eql で始まるリストを指定できます。シンボルを指定すると、そのシンボル (もちろん、型) によって名前が付けられたクラスが指定されます。eql リストは、リスト内のものと等しいオブジェクトで構成される型を指定します。

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

したがって、最初の問題は、シンボル オブジェクトをメソッドに渡すことです。すべてのシンボルはタイプSYMBOLです。この点では、たまたまクラスに名前を付けるシンボルも同じです。クラスとの唯一の関係は、それがクラスの名前であり、サブタイプの関係ではないということです。

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

そのため、インスタンスを使用したり、AMOPを読んだりして、独自の型のジェネリック関数を作成する方法を学ぶ必要があります。

インスタンスを作成したら、次のようにメソッドを記述できます。

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

クラスのインスタンスを取得する簡単な方法がある場合は、次のラッパーを記述できます。

(defmethod fun ((param symbol))
  (fun (retrieve-instance param)))

次に、シンボルを fun に渡して、必要な結果を得ることができます。

AMOP 関数 (標準では指定されていませんが、広く利用可能です。Closer Projectretrieve-instanceを参照してください) を使用する場合は、次のように定義できます。

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

メソッドのディスパッチは、 の結果が役立つ唯一のものであることに注意してくださいclass-prototype。それを変更しようとしないでください。

于 2012-03-03T19:54:12.600 に答える