Inheritance CLOS:如何调用不太具体的方法?
有一种通用方法,比如Inheritance CLOS:如何调用不太具体的方法?,inheritance,methods,common-lisp,reusability,clos,Inheritance,Methods,Common Lisp,Reusability,Clos,有一种通用方法,比如incx。有两个版本的incx。一个专用于类型a,一个专用于类型b。类型b是a的子类。您得到了一个类型为b的对象,即派生类型,但您希望调用类型为a的专用方法。如果在b类型上还没有专门的同名方法,那么您可以很容易地做到这一点,但是,唉,有这样一种方法 那么,在这种情况下,如何调用类型a的专用方法呢 (defclass a () ((x :accessor x :initform 0))) (defclass b (a) ((y :accessor y :initform 0))
incx
。有两个版本的incx
。一个专用于类型a
,一个专用于类型b
。类型b
是a
的子类。您得到了一个类型为b
的对象,即派生类型,但您希望调用类型为a
的专用方法。如果在b
类型上还没有专门的同名方法,那么您可以很容易地做到这一点,但是,唉,有这样一种方法
那么,在这种情况下,如何调用类型a
的专用方法呢
(defclass a () ((x :accessor x :initform 0)))
(defclass b (a) ((y :accessor y :initform 0)))
(defgeneric inc (i))
(defmethod inc ((i a)) (incf (x i)))
(defmethod inc ((i b)) (incf (y i)))
(defvar r (make-instance 'b))
正如CLOS承诺的那样,这将调用最专门的方法:
* (inc r)
* (describe r)
..
Slots with :INSTANCE allocation:
X = 0
Y = 1
但在这种特殊情况下,(不是一般情况下),我想要的是访问不太专业的版本。比如说:
(inc (r a)) ; crashes and burns of course, no function r or variable a
(inc a::r) ; of course there is no such scoping operator in CL
我看到调用下一个方法
函数可以在一个专门化方法中使用,以获得下一个不太专门化的方法,但这不是这里想要的
在这段代码中,我确实需要类似于调用下一个方法
,但用于调用补充方法。我们不需要在下一个不太专业化的类中调用同名的方法,而是需要调用其具有不同名称的互补方法。补充方法也是专门化的,但是调用这个专门化的版本不起作用-原因与调用下一个方法
可能包含的原因大致相同。在超类上专门化的所需方法的名称并不总是相同的
(call-next-method my-complement) ; doesn't work, thinks my-complement is an arg
这里是另一个例子
有一个描述电子特性的基类和一个描述“奇怪电子”特性的派生类。专门研究奇异电子的方法希望调用专门研究电子的方法。为什么?因为这些方法为程序做了正常电子部分的工作。奇怪电子的非电子部分几乎是微不足道的,或者更确切地说,如果它没有复制电子代码:
(defgeneric apply-velocity (particle velocity))
(defgeneric flip-spin (particle))
;;;; SIMPLE ELECTRONS
(defclass electron ()
((mass
:initform 9.11e-31
:accessor mass)
(spin
:initform -1
:accessor spin)))
(defmacro sq (x) `(* ,x ,x))
(defmethod apply-velocity ((particle electron) v)
;; stands in for a long formula/program we don't want to type again:
(setf (mass particle)
(* (mass particle) (sqrt (- 1 (sq (/ v 3e8)))))))
(defmethod flip-spin ((particle electron))
(setf (spin particle) (- (spin particle))))
;;;; STRANGE ELECTRONS
(defclass strange-electron (electron)
((hidden-state
:initform 1
:accessor hidden-state)))
(defmethod flip-spin ((particle strange-electron))
(cond
((= (hidden-state particle) 1)
(call-next-method)
;; CALL ELECTRON'S APPLY-VELOCITY HERE to update
;; the electron. But how???
)
(t nil)))
;; changing the velocity of strange electrons has linear affect!
;; it also flips the spin without reguard to the hidden state!
(defmethod apply-velocity ((particle strange-electron) v)
(setf (mass particle) (* (/ 8 10) (mass particle)))
;; CALL ELECTRON'S SPIN FLIP HERE - must be good performance,
;; as this occurs in critical loop code, i.e compiler needs to remove
;; fluff, not search inheritance lists at run time
)
这一切归结为一个简单的问题:
如果定义了更专业的方法,如何调用不太专业的方法?可以使用MOP(MetaObect协议)。似乎这正是你想要的 使用
change class
也可能玩一些非常可怕的把戏
请注意,CLO中的方法不是“类上的方法”,而是“泛型函数上的方法”。因此,您不能真正调用“父类中具有不同名称的方法”,只能调用不同的泛型函数。您的问题包含两个问题:
(defclass a () ())
(defclass b (a) ())
(defclass c (b) ())
(defgeneric foo (x)
(:method ((x a)) 0)
(:method ((x b)) (+ (call-next-method) 1))
(:method ((x c)) (* (call-next-method) 2)))
我们有一个类层次结构(a
现在,我们计算类b的适用方法,并使用结果列表定义一个函数,该函数调用b上专门化的foo
有效方法
(destructuring-bind (method . next)
(closer-mop:compute-applicable-methods-using-classes
#'foo
(list (find-class 'b)))
(let ((fn (closer-mop:method-function method)))
(defun %foo-as-b (&rest args)
(funcall fn args next))))
这里有两种不同的行为:
(let ((object (make-instance 'c)))
(list
(%foo-as-b object)
(foo object))
=> (1 2)
但是,不建议这样做。
CLOS提供了一种结合有效方法的方法,您应该按照预期使用它,而不是劫持它。
事实上,假设我评估以下内容:
(defmethod foo :before ((i a)) (print "Before A"))
对c的实例c调用的foo
通用函数将打印字符串。但是,当在c上使用%foo-as-b
时,不会打印任何字符串,即使我们调用函数时将c当作b的实例,并且该方法专门用于a
(destructuring-bind (method . next)
(closer-mop:compute-applicable-methods-using-classes
#'foo
(list (find-class 'b)))
(let ((fn (closer-mop:method-function method)))
(defun %foo-as-b (&rest args)
(funcall fn args next))))
这当然是因为使用类计算适用的方法取决于调用时已知的方法集。在这种情况下,函数%foo-as-b
仍在使用过时的方法列表。如果您定义了多个这样的函数或对多个类进行专门化,则效果会更大。如果您希望始终保持%foo-as-b
与您的环境同步,那么您需要在每次调用此函数时重新计算列表(而不是在lambda中重新计算值,而不是让lambda通过)。
另一种可能是在CLO中引入钩子,以便在需要时重新计算函数,但这太疯狂了
不要过度使用继承来共享代码
考虑一下这个问题。
springs的建议是,过度使用继承来共享代码(即实现细节)而不是多态性,这就是“偏爱组合而不是继承”。
看见
和
有关这方面的更多详细信息
使用函数
在C++中,在这里找到代码>::方法()/<代码>,您只需要调用一个具有类似名称的不同函数:当您告诉编译器要调用的方法时,没有动态调度,所以实际上这就像调用一个常规函数。
根据你的要求,我将写以下内容。它基于Dirk的版本,并使用了辅助内联局部函数,当您希望避免重复时,这些函数非常合适:
(defclass electron ()
((mass :initform 9.11e-31 :accessor mass)
(spin :initform -1 :accessor spin)))
(defclass strange-electron (electron)
((hidden-state :initform 1 :accessor hidden-state)))
(let ((light-speed 3e8)
(mysterious-velocity 0d0))
(flet ((%flip (p)
(setf (spin p) (- (spin p))))
(%velocity (p v)
(setf (mass p)
(* (mass p)
(sqrt
(- 1 (expt (/ v light-speed) 2)))))))
(declare (inline %flip %velocity))
(defgeneric flip-spin (particle)
(:method ((p electron))
(%flip p))
(:method ((p strange-electron))
(when (= (hidden-state p) 1)
(call-next-method)
(%velocity p mysterious-velocity))))
(defgeneric apply-velocity (particle velocity)
(:method ((p electron) v)
(%velocity p v))
(:method ((p strange-electron) v)
(setf (mass p)
(* (/ 8 10) (mass p)))
(%flip p)))))
这个问题已经解决了,并且很有希望是可读的:没有必要在CLOS中破解其他东西。由不同方法共享的辅助函数很容易识别,如果需要重新编译其中任何一个,则必须重新编译整个表单
;; Common base class
(defclass electron () ())
;; Actual data for mass and spin
(defclass simple-electron (electron)
((mass :initform 9.11e-31 :accessor mass)
(spin :initform -1 :accessor spin)))
;; A strange electron with a hidden state
(defclass strange-electron (electron)
((simple-electron :accessor simple-electron :initarg :electron)
(hidden-state :initform 1 :accessor hidden-state)))
(macrolet ((delegate (fn &rest args)
`(defmethod ,fn (,@args (e strange-electron))
(funcall #',fn ,@args (simple-electron e)))))
(delegate mass)
(delegate spin)
(delegate (setf mass) new-value)
(delegate (setf spin) new-value))
(defmethod (setf spin) (new-value (e strange-electron))
(funcall #'(setf spin) new-value (simple-electron e)))
(defmethod flip-spin ((e simple-electron))
(setf (spin e) (- (spin e))))
(defmethod apply-velocity ((e simple-electron) velocity)
(setf (mass e)
(* (mass e)
(sqrt
(- 1 (expt (/ velocity +light-speed+) 2))))))
(defmethod flip-spin ((e strange-electron))
(when (= (hidden-state e) 1)
(flip-spin (simple-electron e))
(apply-velocity (simple-electron e) 0d0)))
(defmethod apply-velocity ((e strange-electron) velocity)
(setf (mass e) (* (/ 8 10) (mass e)))
(flip-spin (simple-electron e)))
(defun actually-inc-a (value) (incf (x value)))
(defun actually-inc-b (value) (incf (y value)))
(defmethod inc ((object a)) (actually-inc-a object))
(defmethod inc ((object b)) (actually-inc-b object))
(defun apply-velocity-for-simple-electron (particle v)
(setf (mass particle) (* (mass particle) (sqrt (- 1 (sq (/ v 3e8)))))))
(defun flip-spin-for-simple-electron (particle)
(setf (spin particle) (- (spin particle))))
(defmethod apply-velocity ((particle electron) v)
(apply-velocity-for-simple-electron particle v))
(defmethod flip-spin ((particle electron))
(flip-spin-for-simple-electron particle))
(defmethod apply-velocity ((particle strange-electron) v)
(setf (mass particle) (* (/ 8 10) (mass particle)))
(flip-spin-for-simple-electron particle))
(defmethod flip-spin ((particle strange-electron))
(when (= (hidden-state particle) 1)
(call-next-method)
(apply-velocity-for-simple-electron particle #| Hu? What's the V here? |#)))
(defvar *strange-electron-bypass* nil)
(defmethod flip-spin ((particle strange-electron))
(let ((bypass *strange-electron-bypass*)
(*strange-electron-bypass* nil))
(cond (bypass
(call-next-method))
((= (hidden-state particle) 1)
(call-next-method)
(let ((*strange-electron-bypass* t))
;; where does v come from?
(apply-velocity particle v)))
(t
nil))))
(defmethod apply-velocity ((particle strange-electron) v)
(let ((bypass *strange-electron-bypass*)
(*strange-electron-bypass* nil))
(cond (bypass
(call-next-method))
(t
(setf (mass particle)
(* (/ 8 10) (mass particle)))
(let ((*strange-electron-bypass* t))
(flip-spin particle))))))
(defclass a () ((x :accessor x :initform 0)))
(defclass b (a) ((y :accessor y :initform 0)))
(defgeneric inc (i))
(defgeneric inc-a (i)) ; same as inc, but won't be further specialized
(defmacro inc-a-stuff (i) ; this is not exported! not an interface
`(incf (x ,i))
)
(defmethod inc ((i a)) (inc-a-stuff i))
(defmethod inc ((i b)) (incf (y i)))
;; provides a method to generalize back to class a
;; this method does not get further specialization by b, thus
;; remains a window into the "a part"
(defmethod inc-a ((i a)) (inc-a-stuff i))
(defvar r (make-instance 'b))
(inc r) ; all good, increments y
;;(inc (r a)) ; ah how do you get this?
;;
(inc-a r) ;
(describe r)
#|
Slots with :INSTANCE allocation:
X = 1
Y = 1
|#