一:precedence list
(defclass e() ((ce :initform "ce2" :accessor ce) (ef :initform "ef1" :accessor ef) (eb :initform "eb1" :accessor eb))) (defclass f() ((cf :initform "cf2" :accessor cf) (ef :initform "ef2" :accessor ef) (fb :initform "fb1" :accessor fb))) (defclass c(e f) ((ac :initform "ac2" :accessor ac) (ce :initform "ce1" :accessor ce) (cf :initform "cf1" :accessor cf))) (defclass h() ((bh :initform "bh2" :accessor bh) (hj :initform "hj1" :accessor hj))) (defclass j() ((bj :initform "bj2" :accessor bj) (hj :initform "hj2" :accessor hj))) (defclass b(h j) ((ab :initform "ab2" :accessor ab) (bh :initform "bh1" :accessor bh) (bj :initform "bj1" :accessor bj) (fb :initform "fb2" :accessor fb) (cf :initform "cf2" :accessor cf))) (defclass A(c b) ((ac :initform "ac1" :accessor ac) (ab :initform "ab1" :accessor ab))) CL-USER> (defparameter *kk* (make-instance 'a)) *KK* CL-USER> (fb *kk*) "fb1" CL-USER> (eb *kk*) "eb1" CL-USER> (ce *kk*) "ce1" CL-USER> (ef *kk*) "ef1"
最后你会发现类之间继承的先后顺序是A->C->E->F->B->H->J->standard object->T跟我们平常说的前序查询类似。
对于类的关系比较复杂的话,你首先得画制class hierarchy,原则就是从类开始,然后向上到达直接父类,并且按照直接继承里面父类的顺序从左向右放置在一行,然后按照这种方式依次查找这些类的直接父类。
下面是子类继承的顺序原则,也就是先后顺序。
The precedence list for a class can be computed by traversing the corresponding
network as follows:
1. Start at the bottom of the network.
2. Walk upward, always taking the leftmost unexplored branch.
3. If you are about to enter a node and you notice another path entering the same node from the right, then instead of entering the node, retrace your steps until you get to a node with an unexplored path leading upward. Go back to step 2.
4. When you get to the node representing t , you're done. The order in which you first entered each node determines its place in the precedence list.
广义函数进行继承时的各种形式:
Here is a method for combine that's specialized on numbers:
(defmethod combine ((x number) (y number))
(+ x y))
Methods can even be specialized on individual objects, as determined by eql:
(defmethod combine ((x (eql 'powder)) (y (eql 'spark)))
'boom)
2:standard method combination.
This is called standard method combination. In standard method combination,
calling a generic function invokes
1. The most specific around-method, if there is one.
2. Otherwise, in order,
(a) All before-methods, from most specific to least specific.
(b) The most specific primary method.
(c) All after-methods, from least specific to most specific
The value returned is the value of the around-method (in case 1) or the value of the most specific primary method (in case 2).
CL-USER> (defclass speaker ()()) CL-USER> (defmethod speak ((s speaker) string) (format t "~A" string)) ;;;由实参匹配到最符合的 CL-USER> (speak (make-instance 'speaker) "I'm hungry") I'm hungry NIL CL-USER> (defclass intellectual (speaker) ()) CL-USER> (defmethod speak :before ((i intellectual ) string) (princ "Perhaps ")) CL-USER> (defmethod speak :after ((i intellectual) string) (princ " in some sense")) ;;;利用上面的原则:首先最相关的before到最不相关,然后是最相关的主函数,最后是最不想关到最相关的after CL-USER> (speak (make-instance 'intellectual) "I'm hungry") Perhaps I'm hungry in some sense NIL CL-USER> (defmethod speak :before ((s speaker) string) (princ "I think ")) ;;;始终执行一个最相关的主方法 CL-USER> (speak (make-instance 'intellectual) "I'm hungry") Perhaps I think I'm hungry in some sense NIL CL-USER> (defmethod speak ((i intellectual) string) (format t ", now")) CL-USER> (speak (make-instance 'intellectual) "I'm hungry") Perhaps I think , now in some sense NIL ;;;对于主方法你可以通过call-next-method来调用,比他自己不想关的方法。 CL-USER> (defmethod speak ((i intellectual) string) (format t ", now")) CL-USER> (defmethod speak ((i intellectual) string) (format t ", now") (call-next-method)) CL-USER> (speak (make-instance 'intellectual) "I'm hungry") Perhaps I think , nowI'm hungry in some sense NIL ;;;当遇到around关键字时,你必须有call-next-method才能够继续往下处理 CL-USER> (defclass courtier (speaker) ()) CL-USER> (defmethod speak :around ((c courtier) string) (format t "Does the King believe that ~A? " string) (if (eql (read) 'yes) (if (next-method-p) (call-next-method)) (format t "Indeed, it is a preposterous idea.~%" 'bow))) CL-USER> (speak (make-instance 'courtier) "kings will last") Does the King believe that kings will last? y Indeed, it is a preposterous idea. NIL CL-USER> (speak (make-instance 'courtier) "the world is round") Does the King believe that the world is round? n Indeed, it is a preposterous idea. NIL3 : Method combination
有的时候你会组合所有可以应用的主方法(包括从最匹配到最不匹配)的结果,比方说你想用operator method把所有的结果组合起来,比如你定义了一个price的广义函数现在想用+把所有的可以匹配的方法的结果结合起来,也就是下面的形式,其中args表示的实参,因为你需要根据实参找到precedence list.
我们可以在广义函数里面用关键字:method-combination来指定the type of method combination。
CL-USER> (defgeneric price (x) (:method-combination +))
现在+就作为了method combination,然后所有的price方法必须把+作为第二个参数
CL-USER> (defclass jacket () ()) CL-USER> (defclass trousers () ()) CL-USER> (defgeneric price (x) (:method-combination +)) CL-USER> (defclass suit (jacket trousers) ()) CL-USER> (defmethod price + ((jk jacket)) 350) CL-USER> (defmethod price + ((tr trousers)) 200) CL-USER> (price (make-instance 'suit)) 550
从上面可以发现这些主函数是自动调用的,从最关联到最不关联,不需要再用call-next-method 也千万不要用,否则会报错。当时你仍旧可以用around,像前面一样,必须用call-next-method才能够调用primary method。同时注意:before :after方法不能再用了,详见下面的例子:
CL-USER> (defmethod price :around((st suit)) (format t "~s~%" "around")) CL-USER> (price (make-instance 'suit)) "around" NIL ;;;仍旧需要around 调用call-next-method来调用主函数, CL-USER> (defmethod price :around((st suit)) (format t "~s~%" "around") (call-next-method))上面只是一个operator +的实现方式,我们还有一些其他的method combine。+ append and list max min nconc or progn
2:定义完defmethod,然后我们根据实参,原则:如果defmethod中的特化符是我们提供的实参的类名或他的一个基类的名字。我们就认为是匹配的。
3:根据我前面如何得到predecence list的标准进行排序。
4:从那些applicable method中找到含有:before :after :around关键字的方法。然后根据standard method combine进行处理。 这也给我们一个信息就是所有的函数先看实参匹配,最后才是这些combine。
5:注意其它method combine的构造方式。