CLOS:如何调用不太具体的方法? [英] CLOS: how to call a less specific method?

查看:82
本文介绍了CLOS:如何调用不太具体的方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有一个通用方法,例如incx. incx有两个版本.一种专门针对类型a,另一种专门针对类型b.类型ba的子类.给您一个类型为b的对象,该对象为派生类型-但您想调用专门用于类型a的方法.如果还没有专门针对类型b的同名方法,那么您可以轻松地做到这一点,但是可惜,确实有这样一种方法.

There is a generic method, say incx. There are two versions of incx. One specialized on type a, and one specialized on type b. Type b is a subclass of a. You are given an object of type b, the derived type - but you want to call the method that is specialized on type a. You could do this easily if there wasn't already a method of the same name specialized on type b, but alas, there is such a method.

那么在这种情况下如何调用专门针对类型a的方法?

So how do you call the method specialized on type a in such a situation?

(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所承诺的那样,这称为最专业的方法:

As promised by CLOS, this calls the most specialized method:

* (inc r) 
* (describe r)
    ..
  Slots with :INSTANCE allocation:
    X  = 0
    Y  = 1

但是在这种特殊情况下(不是一般而言),我想要的是访问不太专业的版本.像这样说:

But this in this particular case, (not in general) what I want is to access the less specialized version. Say something like:

(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函数可以从一种专门方法中使用,以获得下一个不太专门的方法,但这不是这里想要的.

I see the call-next-method function can be used from within a specialized method to get the next less specialized method, but that isn't what is wanted here.

在被切掉的代码中,我确实需要类似于call-next-method的东西,但是要调用互补方法.与其在下一个不太专门的类中调用相同名称的方法,我们需要调用其互补方法,该方法具有不同的名称.补充方法也是专门的,但是调用此专门版本不起作用-出于与call-next-method可能包含的原因大致相同的原因.并非总是如此,专用于超类的必需方法具有相同的名称.

In the code this was cut out of, I do need something similar to call-next-method, but for calling a complementary method. Rather than calling a method of the same name in the next less specialized class, we need to call its complementary method, which has a different name. The complementary method is also specialized, but calling this specialized version doesn't work - for much the same reasons that call-next-method was probably included for. It isn't always the case that the required method specialized on the super class has the same name.

(call-next-method my-complement)  ; doesn't work, thinks my-complement is an arg

这是另一个示例.

有一个描述电子性质的基类和一个描述奇电子"性质的派生类.专门针对奇异电子的方法希望调用专门针对电子的方法.为什么?因为这些方法执行程序的正常电子部分.奇怪的电子的非电子部分几乎是微不足道的,或者如果它不复制电子代码,则可能是微不足道的:

There is a base class describing electron properties and a derived class describing the properties of a "strange-electron". Methods specialized on the strange electron desire to call methods specialized on the electron. Why? because these methods do the normal electron part of the work for the program. The non-electron part of the strange electron is almost trivial, or rather it would be if it didn't duplicate the electron code:

(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
  )

所有这些都简化为一个简单的问题:

It all reduces to a simple question:

如果已定义了一种更专门的方法,该如何调用呢?

推荐答案

Dirk的答案有两个可以解决的问题,如下所示.

Dirk's answer has a couple of problems which can be fixed, as shown here.

首先,它不会在不成为新的静态对象系统的情况下泛化.尝试进行泛化时,一个事实会很快发生,即属于同一泛型定义的所有方法都具有相同的名称.为了解决这个问题,只需要给函数赋予名称以反映它们的类型签名即可(根据Stroustrup著名的宏处理器).

Firstly, it does not generalize without becoming a new static object system. When attempting generalization, one quickly runs into the fact that all methods belonging to the same generic definition have the same name. In order to fix this problem one is left to give the functions munged names reflecting their type signature (as per Stroustrup's famous macro processor).

第二,当泛化时,它将成为一个单独的静态面向对象的系统.作为静态系统,它不能很好地与CLOS配合使用.它成为混合范例的一个例子.

Secondly, when generalized it becomes a separate static object oriented system. As a static system it does not play well with CLOS. It becomes a case of mixed paradigms.

但是,避免将代码重复的Dirks方法可以保留在本地,而无需将辅助例程导出到接口.可以通过将它们包装在CLOS方法中来完成.然后,这些CLOS方法成为专门化树中的分支,可以与其他分支分开进行专门化.然后,名称更改代表分支而不是类型签名(更易于管理).

However, Dirks approach of avoiding code duplication can be kept local without exporting the auxiliary routines to the interface. This can be accomplished by wrapping them in CLOS methods. These CLOS methods then become branches in the specialization tree, one's that can be specialized separately from other branches. The name change then represents a branch rather than a type signature (more manageable).

因此,这是应用于inc示例的封装辅助功能方法.请注意,inc-a成为一种不太专门的函数,可以由其他人调用,包括专门针对继承的b类的方法,因为b类中没有任何方法可以进一步专门化它(与inc不同).

So here is the encapsulated auxiliary function approach applied to the inc example. Note that inc-a becomes a less specialized function that can be called by others, including methods specialized on the inherited b class, as no methods in the b class specialize it further (unlike for inc).

(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
|#

此解决方案对于对象架​​构的动态更改是无害的. IE.它可以在CLOS中使用.

This solution is hazard free for dynamic changes to the object schema. I.e. it works within CLOS.

这篇关于CLOS:如何调用不太具体的方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆