本周我正在学习Lisp,我正在阅读优秀的Practical Common Lisp书。第二章介绍如何创建一个简单的CD数据库,首先是天真的方式,然后用宏删除代码重复。具体来说,这个:
(defun where (&key title artist rating (ripped nil ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) t)
(if artist (equal (getf cd :artist) artist) t)
(if rating (equal (getf cd :rating) rating) t)
(if ripped-p (equal (getf cd :ripped) ripped) t))))
成为这个:
(defun make-comparison-expr (field value)
`(equal (getf cd ,field) ,value))
(defun make-comparisons-list (fields)
(loop while fields
collecting (make-comparison-expr (pop fields) (pop fields))))
(defmacro where (&rest clauses)
`#'(lambda (cd) (and ,@(make-comparisons-list clauses))))
现在我试图以同样的方式转换另一个函数:
(defun update (selector-fn &key title artist rating (ripped nil ripped-p))
(setf *db*
(mapcar
#'(lambda (row)
(when (funcall selector-fn row)
(if title (setf (getf row :title) title))
(if artist (setf (getf row :artist) artist))
(if rating (setf (getf row :rating) rating))
(if ripped-p (setf (getf row :ripped) ripped)))
row)
*db*)))
我写的:
(defun make-assignment-expr (field value)
`(setf (getf row ,field) ,value))
(defun make-assignments-list (fields)
(loop while fields
collecting (make-assignment-expr (pop fields) (pop fields))))
(defmacro update (selector-fn &rest assignments)
(setf *db*
(mapcar
`#'(lambda (row)
(when (funcall selector-fn row)
,@(make-assignments-list assignments))
row)
*db*)))
但是,当我编译它时,它会返回以下警告(虽然编译失败)(并且它显示了两次):
warning:
Derived type of (LIST #:G20 #:G21) is
(VALUES CONS &OPTIONAL),
conflicting with its asserted type
(OR FUNCTION SYMBOL).
See also:
SBCL Manual, Handling of Types [:node]
--> LET
==>
(SB-KERNEL:%COERCE-CALLABLE-TO-FUN
`#'(LAMBDA (ROW)
(WHEN (FUNCALL SELECTOR-FN ROW) ,@(MAKE-ASSIGNMENTS-LIST ASSIGNMENTS))
ROW))
任何人都可以解释问题是什么以及如何解决它? 提前谢谢。
答案 0 :(得分:3)
Derived type of (LIST #:G20 #:G21) is
(VALUES CONS &OPTIONAL),
conflicting with its asserted type
(OR FUNCTION SYMBOL).
您提供的值为CONS
; (values ... &optional)
位是一种说法,只有一个值,而不是多个值。但是,您正在调用一个函数,该函数需要函数对象或符号。有罪的电话是以下一个:
(SB-KERNEL:%COERCE-CALLABLE-TO-FUN
`#'(LAMBDA (ROW)
(WHEN (FUNCALL SELECTOR-FN ROW) ,@(MAKE-ASSIGNMENTS-LIST ASSIGNMENTS))
ROW))
您可以尝试通过宏扩展代码来查看它的来源,但是在这里您已经识别出您的lambda形式,并且显然该值不是您期望的值。
您引用#'(lambda (...))
,表示(function (lambda (...)))
。
由于您正在引用它,因此您将获得列表,其中function
符号作为第一个元素,后跟子列表等。因此,这是报告错误的来源。顺便说一下,#'(lambda ())
没用,你可以直接写(lambda ())
,但如果你引用它,问题就会一样。
宏的目的是转换代码;有时您可能想要修改编译环境,但这不是这种情况。当我查看您的宏时,您在不引用它的情况下调用setf
这一事实是一个重大的危险信号:
(defmacro update (selector-fn &rest assignments)
(setf *db*
(mapcar
`#'(lambda (row)
(when (funcall selector-fn row)
,@(make-assignments-list assignments))
row)
*db*)))
您应该将反引号移到setf
并取消引用selector-fn
。