Inheritance 菱形继承与公共Lisp对象系统

Inheritance 菱形继承与公共Lisp对象系统,inheritance,lisp,common-lisp,diamond-problem,clos,Inheritance,Lisp,Common Lisp,Diamond Problem,Clos,我试图找到一个解决常见Lisp CLOS中典型菱形继承问题的方法。守则: (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)) ...) (defme

我试图找到一个解决常见Lisp CLOS中典型菱形继承问题的方法。守则:

(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的代码位于我无法访问的库中,因此我无法修改其中的任何内容。此外,假设其他一些类也将从C2.2派生,可能不想调用m1的C2.2版本,因此我无法使用
:before
向C2.2添加任何内容。使用
调用下一个方法
将调用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
    ...
C1类0:
def m1(自身):。。。
类别C2_1(C1_0):
def m1(自身):。。。
C2类2(C1类0):
def m1(自身):。。。
类别C3_0(C2_1,C2_2):
def m1(自):

C2_2.m1(self)#我在Clozure CL中找到了一种方法,但不确定它是否可以跨主要的公共Lisp实现进行移植(或半移植)

以下代码将调用m1的C2.2版本:

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

可以通过更改超类的顺序来更改分派顺序:

(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

如果你想调用类的特定方法,那你就违背了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中,标准方法组合中的适用方法可以通过
调用next method
调用next method。这是直接调用继承功能的常用机制(也有
:before
:after
:around
方法可以提供继承功能)

但是另一种(简单的)方法组合呢,比如
+
progn
或者
?在
+
的情况下,将调用所有适用的方法并添加结果

然后我们有如下方法:

(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通过将相同的插槽合并到一个插槽中来避免对插槽执行此操作。CLOS通过提供一种不同的方式将方法组织到泛型类中并调用它们,从而避免了方法的这种情况。首先,通过方法组合将它们组装起来。

如果我想在其他函数中使用原始顺序,该怎么办?非常感谢您的澄清。我还在学习Lisp,并且来自Python和C++等语言背景,有时我会忘记CLOS与“通常”面向对象系统的区别,我试图最小化,但不够成功: