带有可选参数和关键字参数的defgeneric



我想在CL中定义一个泛型函数,它接受一个可选参数和一个关键字参数,这两个参数都有默认值。我试着

(defgeneric read-one (buffer &optional (sz 1) &key (signed '()))
可选参数说明符#1=(sz1)

那么做这类事情的正确方法是什么呢?

恐怕你不能在defgeneric中提供默认值。您必须在具体实现(defmethod)

中这样做。
(defgeneric read-one (buffer &optional sz &key signed))
(defmethod read-one (buffer &optional (sz 1) &key (signed '()))
(format t "~a, ~a, ~a~%" buffer sz signed))
CL-USER> (read-one (list 1 2 3) )
;; (1 2 3), 1, NIL
;; NIL
;; CL-USER> (read-one (list 1 2 3) 101 :signed t)
;; (1 2 3), 101, T
;; NIL

你不能在通用函数lambda列表(或&aux之类的东西)中提供默认值或提供-p选项。

所以要提供默认值,你需要在一个方法中这样做。如果你有很多主方法,这可能会很痛苦,所以如果你想让主方法之间的默认值是通用的,一个好方法是在一个方法中包装主方法。

在标准方法组合中,很容易使用around方法:

(defgeneric foo (x &optional y &key z)
(:method :around (x &optional (y 1) &key (z 0))
(call-next-method x y :z z)))   
(defmethod foo (x &optional y &key z)
;; This method can assume things have been defaulted as can any
;; other principal method
(values x y z))

现在

> (foo nil)
nil
1
0
> (foo nil 4 :z 12)
nil
4
12

但是这可能会有问题,因为另一个around方法总是有可能被包裹在任何给定的around方法中,因为around方法是以最具体的第一顺序运行的:

(defmethod foo :around ((x number) &optional (y 3) &key (z 8))
(call-next-method x y :z z))

现在和

> (foo nil)
nil
1
0
> (foo 1)
1
3
8

有时这正是你想要的:你可能希望默认值依赖于位置参数的类。但有时你想要的是能够说'这些是默认值,没有什么可以覆盖它们'。标准的方法组合不能这样做。

要做到这一点,你需要wrapping-standard方法组合或类似的东西。使用它,你可以这样做:

(defgeneric foo (x &optional y &key z)
(:method-combination wrapping-standard)
(:method :wrapping (x &optional (y 1) &key (z 0))
(call-next-method x y :z z)))
(defmethod foo (x &optional y &key z)
(values x y z))

这个包装方法不能被重写:

(defmethod foo :around ((x number) &optional (y 3) &key (z 8))
(call-next-method x y :z z))

但仍:

> (foo nil)
nil
1
0
> (foo 1)
1
1
0

注意我没有写wrapping-standard,但我认为它是有用的。