Common Lisp中defgeneric定义带默认值可选及关键字参数方法
错误原因
ANSI Common Lisp 标准对 defgeneric 泛型声明的 lambda 列表有单独语法约束:和 defun 定义普通函数、defmethod 定义方法的 lambda 列表不同,defgeneric 的 lambda 列表不支持为可选参数(&optional 修饰)、关键字参数(&key 修饰)直接编写默认值初始化形式。
你编写的代码中 (sz 1) 是带默认值的可选参数说明符,不符合 defgeneric 的语法规则,因此会抛出 Invalid &OPTIONAL argument specifier 错误。
正确实现方案
泛型函数本身仅负责约定参数传递规范(必选/可选/关键字参数的位置、名称),默认值逻辑需要下沉到方法层实现。最通用的做法是在 defgeneric 中直接定义一个所有参数特化到 t 的默认方法,统一配置默认值,行为和普通函数带默认参数完全一致:
;; 泛型声明仅约定参数结构,不编写默认值 (defgeneric read-one (buffer &optional sz &key signed) ;; 默认方法中定义参数默认值,匹配所有无更具体特化实现的调用 (:method (buffer &optional (sz 1) &key (signed '())) ;; 此处编写通用业务逻辑即可 (list :buffer buffer :size sz :signed signed)))
调用效果
;; 不传可选参数、关键字参数,自动应用默认值 (read-one "demo-buffer") ;; 返回 => (:BUFFER "demo-buffer" :SIZE 1 :SIGNED NIL) ;; 仅传入可选参数sz,关键字参数走默认值 (read-one "demo-buffer" 16) ;; 返回 => (:BUFFER "demo-buffer" :SIZE 16 :SIGNED NIL) ;; 全参数传入 (read-one "demo-buffer" 32 :signed t) ;; 返回 => (:BUFFER "demo-buffer" :SIZE 32 :SIGNED T)
编写规范提示
- 后续为不同参数类型编写特化方法时,方法的 lambda 列表需要和泛型声明的参数约定兼容,保持可选参数、关键字参数的位置和名称对应。
- 特化方法中可以根据场景重写对应参数的默认值,也可以通过
call-next-method复用默认方法的已有逻辑。
内容的提问来源于stack exchange,提问作者Samantha Atkins
相关产品推荐
相关产品推荐

