2014-11-22 65 views
4

我試圖找到Common Lisp的CLOS解決典型的菱形繼承問題。代碼:鑽石繼承和Common Lisp的對象系統

(defclass C1.0() ...) 
(defclass C2.1 (C1.0) ...) 
(defclass C2.2 (C1.0) ...) 
(defclass C3.0 (C2.1 C2.2) ...) 

(defmethod m1 ((obj C1.0)) ...) 
(defmethod m1 ((obj C2.1)) ...) 
(defmethod m1 ((obj C2.2)) ...) 
(defmethod m1 ((obj C3.0)) 
    ; Here I want to call the C2.2 version of 
    ; m1 
    ...) 

此外,假定C1.0,C2.1和C2.2的代碼中,我沒有使用圖書館,所以我不能有任何修改。 Furthur,假設一些其他類也將從C2.2派生和可能不想打電話M1的C2.2版本,因此使用:before我真的不能添加任何C2.2。使用call-next-method將調用C2.1版本。

只是爲了澄清,這是怎麼解決它在Python:

class C1_0 : 
    def m1 (self) : ... 

class C2_1 (C1_0) : 
    def m1 (self) : ... 

class C2_2 (C1_0) : 
    def m1 (self) : ... 

class C3_0 (C2_1, C2_2) : 
    def m1 (self) : 
    C2_2.m1 (self)  # <-- This is the solution 
    ... 

回答

7

您正在破壞CLOS的目的的具體方法。還要注意,CLOS比這更一般,因爲它不僅支持多繼承,而且還支持多派。調度可以處理多個參數。因此,方法不屬於類,方法的繼承不是基於類繼承,而是基於方法組合(通常使用類繼承以某種方式排序方法)。

讓我們來看看這對派遣兩個參數的方法:

(defmethod m1 ((obj1 C1.0) (obj2 C1.0)) ...) 
(defmethod m1 ((obj1 C2.1) (obj2 C1.0)) ...) 
(defmethod m1 ((obj1 C2.2) (obj2 C3.0)) ...) 
(defmethod m1 ((obj1 C3.0) (obj2 C3.0)) ...) 

請注意,我們談論標準方法組合,這裏通常是最具體的主要方法被調用,當你調用通用函數。在CLOS在標準方法組合適用的方法可以通過call-next-method調用方法。這是通常的機構直接調用繼承的功能(也有可能提供繼承的功能:before:after:around方法)。

但是另一個(簡單)方法組合如+,prognand?在+的情況下,將調用所有可用的方法並添加結果。

然後,我們有這樣的方法:這樣是不是經常在應用程序或庫使用

(defmethod m1 + ((obj1 C1.0) (obj2 C1.0)) 1) 
(defmethod m1 + ((obj1 C2.1) (obj2 C1.0)) 2) 
(defmethod m1 + ((obj1 C2.2) (obj2 C3.0)) 10) 
(defmethod m1 + ((obj1 C3.0) (obj2 C3.0)) 20) 

簡單的方法組合。 CLOS還提供了一種方法用於用戶指定的複合體的方法的組合(例如:設計由合同功能可以由在CLOS用戶來實現)。

你可以解決它的CLOS:您可以訪問一個泛型函數的具體方法和調用它的方法的功能,但是這是很少使用,已經是元級的功能。您也可以編寫自己的方法組合,您可以在其中提供更復雜的調用方法。但這也很困難。

因此,只有在考慮面向類的對象系統和嘗試使用CLOS時,在CLOS中考慮「鑽石問題」纔有意義 - 這可能會將CLOS的使用限制爲簡單案例。但隨後CLOS沒有解決方案鑽石問題。 CLOS通過將相同的插槽合併爲一個插槽來避免它。 CLOS通過提供一種不同的方法將方法組織到泛型中並調用它們,通過首先通過方法組合進行組裝,從而避免了這種方法。

+0

非常感謝您的澄清。我仍然在學習LISP,並且來自Python和C++等語言的背景,有時我傾向於忘記CLOS與「常見」面向對象系統的區別,我嘗試儘量減少,但不夠成功:) – RKS 2014-11-22 18:07:54

0

我發現在Clozure CL的方式,但不知道它的便攜式(或半便攜式)各主要的Common Lisp實現。

下面的代碼將調用M1的C2.2版本:

(funcall 
    (method-function (find-method #'m1 '() `(,(find-class 'C2.2)))) 
    obj) 
0

調度的順序可以通過改變超類的順序被改變:如果你想打電話給

(defclass c3.2 (c2.2 c2.1) 
    ...) 

(defclass c3.1 (c2.1 c2.2) 
    ...) 

(m1 (make-instance 'c3.2)) ; calls the method specialized to c2.2 

(m1 (make-instance 'c3.1)) ; calls the method specialized to c2.1 
+0

如果我想在其他功能的原始順序呢? – RKS 2014-11-22 07:43:01