【问题标题】:Does this CLOS code result in a runtime or a compile time error in Common Lisp?此 CLOS 代码是否会导致 Common Lisp 中的运行时或编译时错误?
【发布时间】:2015-07-10 05:34:06
【问题描述】:

假设我编写了以下代码(请原谅任何错误,我是 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

按照我的预期,它应该可以毫无怨言地打印出 cmets 中所写的内容。

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

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

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

【问题讨论】:

  • 我猜你想让第一个 defmethod 专门针对 o 的论点,对吧?
  • 你应该使用make-instance而不是allocate-instance,并且它需要一个符号,所以你需要在字面上引用它。
  • 我只使用规范编写代码,所以我可能会犯一些错误。谢谢。

标签: common-lisp multiple-inheritance overriding clos


【解决方案1】:

这应该表示一个错误,according to the spec in 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) 会将a 传递给m (b),而m (b) 不是b

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

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

【讨论】:

  • 由于某种原因,调用(m aa) 导致SBCL 中出现“AOO”,知道为什么m(o) 会被调用两次吗?
  • 它没有。它在m (a) 中打印"A",然后在m (o) 中打印"O",然后REPL 打印结果,即print 的参数,即字符串"O"。所以,你看到的两个"O"s 是 REPL 的副作用。为了证明这一点,请尝试调用(progn (m aa) (values)),或在m (o) 中添加(values) 作为最后一个形式。
【解决方案2】:

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

mc 的对象的顺序是:

(c a b o)

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

(a o)

由于这些有不同的方法顺序call-next-method 需要发出错误信号。写这篇文章的part of CLHS

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

解决方法是不向call-next-method 添加参数。编辑后,您可以在调用 (m cc) 时将其打印出来:

"A" 
"B" 
"O" 

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

【讨论】:

  • 它不应该在编译时失败。它应该在运行时失败,但正如您所见,当指定不同的参数时,某些实现不遵循call-next-method 规则,因此在实践中,它可能不会在运行时失败。
  • @PauloMadeira 这样做是不合理的,但规范中没有任何反对它的东西。我就是这么读的“错误检查的优化是可能的,但它们不能改变 call-next-method 的语义。”.
  • 但这是关于 call-next-method 自己的行为,而不是编译调用它的行为。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多