Common Lisp 对象系统方法执行顺序
Common Lisp Object System method execution order
我有以下两个类:
(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!"
我想弄清楚的是这些方法是如何根据 "order." 执行的,我似乎无法理解正在发生的事情以及原因。据说有一个规则优先于此,但我不确定在哪里可以找到它。例如,在这种情况下,为什么 "Hello!Can I help you"
永远不会触发?
当您没有任何 around 方法时,方法应用的顺序是:从最具体到最不具体的所有方法之前,然后是最具体的主要方法,以及然后是从最不具体到最具体的 after 方法。在您的情况下,您有两种主要方法(名称旁边没有 :before 或 :after 的方法),一种指定人,另一种指定说话者。由于 speaker 比 person 更具体,因此仅调用 speaker 主要方法。如果您想调用多个主要方法,请查看 call-next-method.
虽然我看到已经有一个公认的答案,但 Common Lisp 在 HyperSpec 中有一些非常好的文档,知道在哪里可以找到对发生的事情的完整描述是很有用的。在这种情况下,它是 7.6.6.2 Standard Method Combination,表示(缩写):
The semantics of standard method combination is as follows:
If there are any around methods, the most specific around method is called. It supplies the value or values of the generic function.
Inside the body of an around method, call-next-method can be used to call the next method. When the next method returns, the around method
can execute more code, perhaps based on the returned value or values.
The generic function no-next-method is invoked if call-next-method is
used and there is no applicable method to call. The function
next-method-p may be used to determine whether a next method exists.
If an around method invokes call-next-method, the next most specific around method is called, if one is applicable. If there are no around
methods or if call-next-method is called by the least specific around
method, the other methods are called as follows:
All the before methods are called, in most-specific-first order. Their values are ignored. An error is signaled if
call-next-method is used in a before method.
The most specific primary method is called. Inside the body of a primary method, call-next-method may be used to call the next most
specific primary method. When that method returns, the previous
primary method can execute more code, perhaps based on the returned
value or values. The generic function no-next-method is invoked if
call-next-method is used and there are no more applicable primary
methods. The function next-method-p may be used to determine whether a
next method exists. If call-next-method is not used, only the most
specific primary method is called.
All the after methods are called in most-specific-last order. Their values are ignored. An error is signaled if call-next-method is
used in an after method.
If no around methods were invoked, the most specific primary method supplies the value or values returned by the generic function. The
value or values returned by the invocation of call-next-method in the
least specific around method are those returned by the most specific
primary method.
该页末尾有一个特别有用的插图,描述了行为及其动机:
The before methods are run in most-specific-first order while the
after methods are run in least-specific-first order. The design
rationale for this difference can be illustrated with an example.
Suppose class C1 modifies the behavior of its superclass, C2, by
adding before methods and after methods. Whether the behavior of the
class C2 is defined directly by methods on C2 or is inherited from its
superclasses does not affect the relative order of invocation of
methods on instances of the class C1. Class C1's before method runs
before all of class C2's methods. Class C1's after method runs after
all of class C2's methods.
By contrast, all around methods run before any other methods run. Thus
a less specific around method runs before a more specific primary
method.
If only primary methods are used and if call-next-method is not used,
only the most specific method is invoked; that is, more specific
methods shadow more general ones.
除了其他答案,请注意您可以使用以下宏定义自定义方法组合:
DEFINE-METHOD-COMBINATION
. There are already ten existing method combinators 所以我认为定义自定义的并不常见。当然,有时能够这样做非常有用(请参阅 Joshua Taylor 的评论)。
此外,您的方法的调用方式受制于 class 继承,默认情况下会考虑父子关系,以及 superclass 之间的顺序es。请阅读"Fundamentals of CLOS". The class precedence list can be changed with the Meta-Object Protocol: see COMPUTE-CLASS-PRECEDENCE-LIST
。
我有以下两个类:
(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!"
我想弄清楚的是这些方法是如何根据 "order." 执行的,我似乎无法理解正在发生的事情以及原因。据说有一个规则优先于此,但我不确定在哪里可以找到它。例如,在这种情况下,为什么 "Hello!Can I help you"
永远不会触发?
当您没有任何 around 方法时,方法应用的顺序是:从最具体到最不具体的所有方法之前,然后是最具体的主要方法,以及然后是从最不具体到最具体的 after 方法。在您的情况下,您有两种主要方法(名称旁边没有 :before 或 :after 的方法),一种指定人,另一种指定说话者。由于 speaker 比 person 更具体,因此仅调用 speaker 主要方法。如果您想调用多个主要方法,请查看 call-next-method.
虽然我看到已经有一个公认的答案,但 Common Lisp 在 HyperSpec 中有一些非常好的文档,知道在哪里可以找到对发生的事情的完整描述是很有用的。在这种情况下,它是 7.6.6.2 Standard Method Combination,表示(缩写):
The semantics of standard method combination is as follows:
If there are any around methods, the most specific around method is called. It supplies the value or values of the generic function.
Inside the body of an around method, call-next-method can be used to call the next method. When the next method returns, the around method can execute more code, perhaps based on the returned value or values. The generic function no-next-method is invoked if call-next-method is used and there is no applicable method to call. The function next-method-p may be used to determine whether a next method exists.
If an around method invokes call-next-method, the next most specific around method is called, if one is applicable. If there are no around methods or if call-next-method is called by the least specific around method, the other methods are called as follows:
All the before methods are called, in most-specific-first order. Their values are ignored. An error is signaled if call-next-method is used in a before method.
The most specific primary method is called. Inside the body of a primary method, call-next-method may be used to call the next most specific primary method. When that method returns, the previous primary method can execute more code, perhaps based on the returned value or values. The generic function no-next-method is invoked if call-next-method is used and there are no more applicable primary methods. The function next-method-p may be used to determine whether a next method exists. If call-next-method is not used, only the most specific primary method is called.
All the after methods are called in most-specific-last order. Their values are ignored. An error is signaled if call-next-method is used in an after method.
If no around methods were invoked, the most specific primary method supplies the value or values returned by the generic function. The value or values returned by the invocation of call-next-method in the least specific around method are those returned by the most specific primary method.
该页末尾有一个特别有用的插图,描述了行为及其动机:
The before methods are run in most-specific-first order while the after methods are run in least-specific-first order. The design rationale for this difference can be illustrated with an example. Suppose class C1 modifies the behavior of its superclass, C2, by adding before methods and after methods. Whether the behavior of the class C2 is defined directly by methods on C2 or is inherited from its superclasses does not affect the relative order of invocation of methods on instances of the class C1. Class C1's before method runs before all of class C2's methods. Class C1's after method runs after all of class C2's methods.
By contrast, all around methods run before any other methods run. Thus a less specific around method runs before a more specific primary method.
If only primary methods are used and if call-next-method is not used, only the most specific method is invoked; that is, more specific methods shadow more general ones.
除了其他答案,请注意您可以使用以下宏定义自定义方法组合:
DEFINE-METHOD-COMBINATION
. There are already ten existing method combinators 所以我认为定义自定义的并不常见。当然,有时能够这样做非常有用(请参阅 Joshua Taylor 的评论)。
此外,您的方法的调用方式受制于 class 继承,默认情况下会考虑父子关系,以及 superclass 之间的顺序es。请阅读"Fundamentals of CLOS". The class precedence list can be changed with the Meta-Object Protocol: see COMPUTE-CLASS-PRECEDENCE-LIST
。