【问题标题】:Common Lisp Object System method execution orderCommon Lisp 对象系统方法执行顺序
【发布时间】:2015-06-05 00:37:46
【问题描述】:

我有以下两个类:

(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" 在这种情况下不会触发?

【问题讨论】:

标签: lisp common-lisp clos


【解决方案1】:

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

【讨论】:

    【解决方案2】:

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

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

    • 如果有任何环绕方法,则调用最具体的环绕方法。它提供泛型函数的一个或多个值。

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

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

      • 所有之前的方法都以最具体的优先顺序被调用。它们的值被忽略。如果发生错误,则会发出错误信号 call-next-method 用在 before 方法中。

      • 调用最具体的主要方法。在主要方法的主体内,可以使用 call-next-method 调用下一个最 具体的初级方法。当该方法返回时,前一个 primary 方法可以执行更多代码,可能基于返回的 价值或价值。通用函数 no-next-method 被调用,如果 使用了 call-next-method 并且没有更多适用的主要方法 方法。函数 next-method-p 可用于确定一个 下一个方法存在。如果不使用 call-next-method,只使用最 调用特定的主要方法。

      • 所有 after 方法都以最具体的最后顺序调用。它们的值被忽略。如果 call-next-method 是一个错误信号 在 after 方法中使用。

    • 如果没有调用周围方法,则最具体的主要方法提供泛型函数返回的一个或多个值。这 中调用 call-next-method 返回的一个或多个值 最不具体的周围方法是由最具体的方法返回的 主要方法。

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

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

    相比之下,all around 方法在任何其他方法运行之前运行。因此 一个不太具体的环绕方法在一个更具体的主之前运行 方法。

    如果只使用主要方法并且不使用 call-next-method, 只调用最具体的方法;也就是说,更具体 方法会影响更一般的方法。

    【讨论】:

      【解决方案3】:

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

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

      【讨论】:

      • “我认为这在实践中并不常用。”它可能不是全部那么普遍,尤其是因为有a number of useful ones built in,但是当有机会使用它时,它非常有用。
      猜你喜欢
      • 2015-01-20
      • 1970-01-01
      • 1970-01-01
      • 2011-06-03
      • 1970-01-01
      • 2021-12-18
      • 1970-01-01
      • 2019-01-23
      • 1970-01-01
      相关资源
      最近更新 更多