1

メソッドオブジェクトを関数として呼び出すにはどうすればよいですか?

Closer-mopclosパッケージはどちらも、メソッド オブジェクトを関数に変換するためのメソッド関数を提供します。ただし、別のパッケージを含めずにそれを行う方法はありますか? そうでない場合、どのパッケージですか?(SBCLを使用)しかし、パッケージが必要な場合、識別機能はどのようにそれを行いますか?

これは、find-method を使用してメソッド オブジェクトを取得する例です。問題は、method-to-be- call を呼び出す方法です。

(defclass a () ((x :accessor x :initform 0)))
(defgeneric inc (i))
(defmethod inc ((i a)) (incf (x i)))
(defvar r (make-instance 'a))

;; ... in a land far far away:    
(defvar method-to-be-called (find-method #'inc '() '(a)))

(funcall method-to-be-called r);; crashes and burns

二次的な質問として、ドキュメントによると、識別関数は最初にクラスごとに計算してメソッドオブジェクトを見つけようとし、それが失敗した場合はcompute-applicable-methodsを使用します。なぜこの 2 層アプローチを採用するのでしょうか。find-methodがこの 2 層のアプローチを行っていると仮定するのは正しいので、 find- methodを使用する方がよいでしょうか?

-- 付録 -- 以下のコメントで、Rainer Joswig は、この検索メソッドの形式は実装に依存していると指摘しました。

(find-method #'inc '() '(a))) ; works on sbcl 1.3.1

彼は、指定子リストはクラスであるべきだと言い、代わりに次のように提案します:

(find-method #'inc '() (list (find-class 'a))))

だから私は自分のクラスをそこに置くことを考えました:

(find-method #'inc '() (list a))  ; crashes and burns

どうやら(defclass a ... )はaをクラスに設定していません。実際、それは何にも設定されていません!

* (defclass a () ((x :accessor x :initform 0)))
#<STANDARD-CLASS COMMON-LISP-USER::A>
* a

... 変数 A はバインドされていません。

ただし、これは機能します。

* (defvar ca (defclass a () ((x :accessor x :initform 0))))
CA
* (defmethod inc ((i a)) (incf (x i)))
WARNING: Implicitly creating new generic function COMMON-LISP-USER::INC.
#<STANDARD-METHOD COMMON-LISP-USER::INC (A) {1005EE8263}>
enter code here
* (find-method #'inc '() (list ca))   
#<STANDARD-METHOD COMMON-LISP-USER::INC (A) {1005EE8263}>
* 

したがって、クラスは、defclass に提供されるシンボルの値ではなく、defclass からの戻り値です。

4

3 に答える 3

4
(find-method #'inc '() '(a))

上記は機能しません。シンボルのリストではなく、クラスのリストが必要です。

(funcall (method-function (find-method #'inc
                                       '()
                                       (list (find-class 'a))))
         r)

この関数method-functionは MOP に属しているため、多くの実装で提供されており、一部の実装固有のパッケージに含まれています。CLOSER-MOPも利用可能にします。

しかし通常、すでにメソッド関数を抽出しようとしている場合は、おそらく CLOS を間違った方法で使用しているか、自分が何をしているかを本当に理解しています...

于 2016-02-05T10:25:48.350 に答える
3

メソッドオブジェクトを関数として呼び出すにはどうすればよいですか?

正直な質問: なぜそうしたいのですか? メソッドの関数がどのように構築されるかを最初に指定しましたか?

より近いモップを使用しても、によって返される関数closer-mop:method-functionは、せいぜいcloser-mop:make-method-lambdaそのラムダリストに関して一貫していると思います。そのため、おそらくパッケージを使用して、移植可能なものを知ることができます。

メソッドの関数は、ジェネリック関数と同じラムダ リストを持つ関数である必要はなく、通常は と によるものではありませnext-method-pcall-next-method。一部の実装では、次のメソッド リストに動的バインディングが使用される場合があるため、これらの実装には、ジェネリック関数と一致するメソッド ラムダ リストが含まれる場合があります。一般的に、それを当てにしないでください。

SBCL はこれらの実装の 1 つではないと思います。次のメソッド リストは、サポートするメソッドの関数に渡されnext-method-pますcall-next-method

なぜこの 2 層アプローチを採用するのでしょうか。

可能であれば、クラスのリストに基づいてメモ化 (またはキャッシュ) できるためです。ジェネリック関数が同じクラスの引数で再度呼び出され、ジェネリック関数が更新されていない場合 (MOP の「従属保守プロトコル」を参照)、それ以上処理せずに最後の結果を再利用できます。キーがクラスのリストであるハッシュテーブルになります。

ただし、compute-applicable-methods-using-classesfalse の秒の値を返す場合は、thencompute-applicable-methodsが使用されます。その理由は、クラスのみを使用してメソッドを見つけることができなかったためです。これは、一部のメソッドにクラス以外のスペシャライザーがあることを意味します。

これは、適用可能なメソッドがないと言っているのとは異なります。たとえば、すべてのメソッドがクラスに特化されていて、適用可能なメソッドがない場合はcompute-applicable-methods-using-classes、空のリストと真の 2 番目の値を返す必要があります。を呼び出しcompute-applicable-methodsても意味がありません。それ以上のものを見つけることはありません (または、適切に実装されている場合はすべきではありません)。

を使用する場合でもメモ化を実行することは可能compute-applicable-methodsですが、メモ化は、たとえば、クラスのリストをハッシュ テーブルのキーとして使用する場合ほど簡単ではなくなります。おそらく、ツリーノードが特殊化可能なパラメーターリスト全体と一致するまで、各スペシャライザー (インスタンス、次にクラス) のメソッドを引数ごとに順番に検索しようとするツリー構造を使用できます。

非標準のスペシャライザーでは、各ノードの検索順序を変更する必要があります。そのようなスペシャリストの優先順位が厳密にeqlクラスの前、間、または後でない限り、未知の領域にいることになります。

実際にはcompute-applicable-methods-using-classes、非標準のスペシャリストを認識して早期に false を返すように変更する必要があります。compute-applicable-methodsとにかく、これらのスペシャリストを処理するように変更する必要があります。とにかくの結果をメモしcompute-applicable-methodsます。

find-method がこの 2 層のアプローチを行っていると仮定するのは正しいので、 find-method を使用する方がよいでしょうか?

いいえ、の目的は特定の方法find-methodを見つけることであり、適用可能な方法ではありません。使っていないか、全くありません。実際、スペシャリストの代わりに実際の引数を取るため、後者を使用することはできませんでした。compute-applicable-methods-using-classescompute-applicable-methods

于 2016-02-05T19:02:43.713 に答える