2012-03-03 72 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

你將不能夠輕鬆地執行僅使用CLOS調度這個確切的任務。

在我繼續之前,我認爲關於術語的一些簡要說明很重要。

的Common Lisp的HyperSpec詞彙defines 「子類」 中這樣說:

從另一個類繼承的類,稱爲超類。 (無 類是自身的子類。)

這個定義,而直觀,似乎有些奇怪我爲我期望這是一個「適當的子類」的定義。然而,所有的類都是類型,它defines「子」爲:

類型,其成員是相同的或其他類型的成員的一個子集,被稱爲超類型。 (每種類型都是它自己的子類型。)

請注意括號:「每種類型都是它自己的子類型。「

它還defines一個‘適當的子類型’:

這是不相同的類型的類型的類型的子類型(的類型)(即,其元素是一個``適當子集'' 的類型)。

所以,在你的例子,B和C是A的子類,並且還亞型。在另一方面B,C,和A是A.

亞型

放在德的東西fmethod是"parameter specializer name"。它可以是一個符號,一個類(這有點難),或者一個以eql開頭的列表。如果您提供了一個符號,它將指定由該符號命名的類(當然,這是一個類型)。一個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))) 

然後你就可以通過符號來獲得樂趣並獲得你想要的結果。

如果你想使用AMOP函數(沒有標準規定,但被廣泛使用,見Closer Project),你可以定義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 

兩個返回值的含義見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)完成。這樣你就不需要創建類的實例來調度。人們只會使用原型實例。

替代的,醜陋的,版本將是硬編碼值:

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

(defmethod fun0 ((param (eql 'c))) 
    T) 
相關問題