2

假设我编写了以下代码(请原谅任何错误,我是 Lisp 新手,我无法在这台机器上运行 CL)

(defclass o () ())
(defclass a (o) ())
(defclass b (o) ())
(defgeneric m (x))
(defmethod m ((x o))
   (print "O")
)
(defmethod m ((x a))
   (print "A")
   (call-next-method (make-instance 'a))
)
(defmethod m ((x b))
   (print "B")
   (call-next-method (make-instance 'b))
)

(setq aa (make-instance 'a))
(m aa) ;prints A, then O
(setq bb (make-instance 'b))
(m bb) ;prints B, then O

根据我的期望,它应该打印评论中写的内容而没有任何抱怨。

如果我添加以下代码会发生什么?

(defclass c (a b) ())
(setq cc (make-instance 'c))
(m cc)

如果我理解标准方法组合,适用的方法cc将被排序为(m a), (m b), (m o), 并且(m b)不会被call-next-method成功调用。但实际上会发生什么?当我定义类c并说它使泛型函数的方法链无效时,CL 会抱怨m吗?还是会出现运行时错误?

4

2 回答 2

1

根据Exceptional Situations中的规范,这应该表示错误:

为 call-next-method 提供参数时,必须满足以下规则,否则应发出错误类型的错误信号: call-next-method 的更改参数集的适用方法的有序集必须与用于泛型函数的原始参数的有序适用方法集。错误检查的优化是可能的,但它们不能改变 call-next-method 的语义。

正如您所说的那样,拓扑类顺序将是:

  • c
  • a
  • b
  • o

文字图:

   o
 /   \
a     b
 \   /
   c

因此,适用的方法列表将是:

  • m (a)
  • m (b)
  • m (o)

因此,如果call-next-method不表示错误,m (a)将传递一个ato m (b),而不是一个b.

根据规范,这不应该发生,但我相信实现可能会出于性能原因选择违反此规则。在每次调用时计算适用方法的成本很高call-next-method

PS:实际上,根据实现,call-next-method可以检查新参数的“顶级”匹配的专家列表是否与原来的相同。为了实现这一点,标准计算判别函数必须更复杂,并且可能会做一些非标准compute-applicable-methods-using-classescompute-applicable-methods可能不会做的副业。

于 2015-04-30T15:21:27.803 回答
1

它可能在编译时失败。它很可能在运行时失败。

类对象的方法m顺序c是:

(c a b o)

它将命中a在新实例上调用下一个方法的位置aa对方法有这个方法顺序m

(a o)

由于这些具有不同的方法顺序,因此call-next-method需要发出错误信号。CLHS中关于此的部分:

为 call-next-method 提供参数时,必须满足以下规则,否则应发出错误类型的错误信号:call-next-method 的已更改参数集的适用方法的有序集必须与用于泛型函数的原始参数的有序适用方法集。错误检查的优化是可能的,但它们不能改变 call-next-method 的语义。

解决方法是不向call-next-method. 在该编辑之后,您可以在调用时将其打印出来(m cc)

"A" 
"B" 
"O" 

在 CLISP 中,它会在运行时发出错误信号,而 SBCL 不会,因此它实际上在 SBCL 中不符合规范。

于 2015-04-30T15:28:14.943 回答