【发布时间】:2018-12-06 19:26:25
【问题描述】:
当我在本书提供的最终产品中遇到错误时,我正在关注 Practical Common Lisp 书中的第一个示例项目。
该项目是一个存储有关 cd 信息的基本数据库。它支持与 where 宏一起使用的 select 和 update 语句。
我已经使用 clisp 和 sbcl 编译器测试了代码,两者都出现了同样的错误。
代码如下:
(defvar *db*)
(defun make-cd (title artist rating ripped)
(list :title title :artist artist :rating rating :ripped ripped))
(defun add-record (cd) (push cd *db*))
(defun dump-db ()
(format t "~{~{~a:~10t~a~%~}~%~}" *db*))
(defun prompt-read (prompt)
(format *query-io* "~a: " prompt)
(force-output *query-io*)
(read-line *query-io*))
(defun prompt-for-cd ()
(make-cd
(prompt-read "Title")
(prompt-read "Artist")
(or (parse-integer (prompt-read "Rating") :junk-allowed t) 0)
(y-or-n-p "Ripped [y/n]: ")))
(defun add-cds ()
(loop (add-record (prompt-for-cd))
(if (not (y-or-n-p "Another? [y/n]: ")) (return))))
(defun save-db (filename)
(with-open-file (out filename
:direction :output
:if-exists :supersede)
(with-standard-io-syntax
(print *db* out))))
(defun load-db (filename)
(with-open-file (in filename)
(with-standard-io-syntax
(setf *db* (read in)))))
(defun select (selector-fn)
(remove-if-not selector-fn *db*))
(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))))
(defun 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 (cd)
(when (funcall selector-fn cd)
(if title (setf (getf cd :title) title))
(if artist (setf (getf cd :artist) artist))
(if rating (setf (getf cd :rating) rating))
(if ripped-p (setf (getf cd :ripped) ripped)))
cd)
*db*)))
(defun delete-rows (selector-fn)
(setf *db* (remove-if selector-fn *db*)))
这是产生错误的调用:
(select (where :artist "Dixie Chicks"))
基本上,这次通话中发生的情况如下。 where 宏给出了一个 lambda 表达式,它接受一个 cd 并作为一个谓词来确定 cd 是否具有某些字段的某些值。
在此特定调用中,where 宏扩展为:
#'(lambda (cd) (and (equal (getf cd :artist) "Dixie Chicks")))
这是 db 当前定义为:
((:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 7 :RIPPED T)
(:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T)
(:TITLE "Lyle Lovett" :ARTIST "Lyle Lovett" :RATING 9 :RIPPED T)
(:TITLE "Give Us a Break" :ARTIST "Limpopo" :RATING 10 :RIPPED T)
(:TITLE "Rockin' the Suburbs" :ARTIST "Ben Folds" :RATING 6 :RIPPED T)
(:TITLE "Naive" :ARTIST "The Kooks" :RATING 6 :RIPPED T)
(:TITLE "It's the end of the world as we know it" :ARTIST "REM" :RATING 6
:RIPPED T)
(:TITLE "We Walk" :ARTIST "REM" :RATING 8 :RIPPED T))
这是上面调用产生的错误:
The value
#'(LAMBDA (CD) (AND (EQUAL (GETF CD :ARTIST) "Dixie Chicks")))
is not of type
(OR FUNCTION SYMBOL)
when binding SB-IMPL::PREDICATE
[Condition of type TYPE-ERROR]
以下是上述调用的预期结果:
((:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 7 :RIPPED T)
(:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T))
在寻求帮助之前,我花了很多时间查看这段代码,但由于我对 Common Lisp 的了解非常原始,我似乎无法找到罪魁祸首。
提前致谢!
【问题讨论】:
-
where应该是defmacro,而不是defun(更改后不要忘记重新编译where的所有用户)。 -
@sds 非常感谢。我发誓我盯着这个看了 2 个小时。哎呀。
标签: common-lisp