Common Lisp Object System方法执行顺序

时间:2015-06-05 00:37:46

标签: lisp common-lisp clos

我有以下两个类:

(defclass person () ())

(defmethod speak ((s person) string)
    (format t "-A" string))

(defmethod speak :before ((s person) string)
    (print "Hello! "))

(defmethod speak :after ((s person) string)
    (print "Have a nice day!"))


(defclass speaker (person) ())

(defmethod speak ((i speaker) string)
  (print "Bonjour!"))


(speak (make-instance 'speaker) "Can I help yoU?")

这个的输出是:

"Hello! "                                                                                                                                                                                                                                               
"Bonjour!"                                                                                                                                                                                                                                              
"Have a nice day!" 

我想弄清楚的是这些方法是如何按照“顺序”执行的。我似乎无法掌握正在发生的事情和原因。据说有一个规则优先于此,但我不知道在哪里找到它。例如,为什么"Hello!Can I help you"在这种情况下不会发射?

3 个答案:

答案 0 :(得分:9)

当你没有任何方法时,方法应用的顺序是:从最具体到最不具体的方法之前的所有方法,然后是最具体的主要方法,然后是来自之后的方法最具体到最具体的。在您的情况下,您有两个主要方法(方法没有:名称旁边的或之前):一个指定人,另一个指定扬声器。由于说话者比人更具体,因此只调用说话者主要方法。如果您想调用多个主要方法,请查看call-next-method

答案 1 :(得分:3)

虽然我发现已经有一个已经接受的答案,但Common Lisp在HyperSpec中有一些非常好的文档,知道在哪里可以找到所发生的事情的完整描述是有用的。在这种情况下,它是 7.6.6.2 Standard Method Combination ,它说(缩写):

  

标准方法组合的语义如下:

     
      
  • 如果有任何around方法,则调用最具体的around方法。它提供通用函数的值。

  •   
  • 在around方法的主体内部,call-next-method可用于调用下一个方法。当下一个方法返回时,around方法   可以执行更多代码,可能基于返回的值或值。   如果call-next-method是,则调用泛型函数no-next-method   使用,没有适用的方法来调用。功能   next-method-p可用于确定是否存在下一个方法。

  •   
  • 如果around方法调用call-next-method,则调用下一个最具体的around方法(如果适用)。如果周围没有   方法或者如果call-next-method被最不具体的方法调用   方法,其他方法调用如下:

         
        
    • 以最具体的第一顺序调用所有之前的方法。他们的价值被忽略了。如果是,则发出错误信号   call-next-method用于before方法。

    •   
    • 调用最具体的主要方法。在主要方法的主体内部,可以使用call-next-method来调用下一个方法   具体的主要方法。当该方法返回时,前一个   主方法可以执行更多代码,也许基于返回   价值或价值。如果是,则调用泛型函数no-next-method   使用call-next-method并且没有更多适用的主要方法   方法。函数next-method-p可用于确定是否a   存在下一种方法。如果不使用call-next-method,则只使用最多   调用特定的主要方法。

    •   
    • 所有after方法都以最具体的最后一个顺序调用。他们的价值被忽略了。如果call-next-method为,则发出错误信号   用于后方法。

    •   
  •   
  • 如果没有调用方法,则最具体的主方法提供泛型函数返回的值。该   调用call-next-method返回的值或值   最具体的方法是最具体的返回方法   主要方法。

  •   

在该页面的末尾有一个特别有用的插图,描述了行为及其动机:

  

之前的方法以最特定的第一顺序运行   方法以最少特定的第一顺序运行之后。该设计   可以用一个例子来说明这种差异的基本原理。   假设类C1修改其超类C2的行为   在方法之前和之后添加方法。是否行为   C2类直接由C2上的方法定义,或者从C2继承   超类不会影响调用的相对顺序   C1类实例的方法。方法运行之前的C1类   在所有C2类方法之前。 C1之后的方法运行   所有C2类的方法。

     

相比之下,所有周围的方法都在任何其他方法运行之前运行。从而   一个不太具体的around方法在更具体的主要方法之前运行   方法

     

如果仅使用主要方法且未使用call-next-method,   只调用最具体的方法;也就是说,更具体   方法影响更普遍的。

答案 2 :(得分:1)

除了其他答案,请注意您可以使用以下宏定义自定义方法组合: DEFINE-METHOD-COMBINATION。已经有10 existing method combinators,所以我认为定义自定义的不常见。当然,能够这样做有时非常有用(参见Joshua Taylor的评论)。

此外,调用方法的方式取决于类继承,默认情况下会考虑父子关系,以及超类之间的顺序。请阅读"Fundamentals of CLOS"。可以使用元对象协议更改类优先级列表:请参阅COMPUTE-CLASS-PRECEDENCE-LIST