当我在本书提供的最终产品中遇到错误时,我正在阅读Practical Common Lisp一书中的第一个示例项目.
该项目是一个存储有关cds信息的基本数据库.它支持select和update语句以及where宏.
我已经使用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*)))
Run Code Online (Sandbox Code Playgroud)
这是产生错误的调用:
(select (where :artist "Dixie Chicks"))
Run Code Online (Sandbox Code Playgroud)
基本上这次电话会议的内容如下.where宏给出一个lambda表达式,该表达式接受cd并作为谓词来确定cd是否具有某些字段的某些值.
在此特定调用中,宏扩展为:
#'(lambda (cd) (and (equal (getf cd :artist) "Dixie Chicks")))
Run Code Online (Sandbox Code Playgroud)
这是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))
Run Code Online (Sandbox Code Playgroud)
以下是上述调用产生的错误:
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]
Run Code Online (Sandbox Code Playgroud)
以下是上述调用的预期结果:
((:TITLE "Fly" :ARTIST "Dixie Chicks" :RATING 7 :RIPPED T)
(:TITLE "Home" :ARTIST "Dixie Chicks" :RATING 9 :RIPPED T))
Run Code Online (Sandbox Code Playgroud)
在寻求帮助之前,我花了很多时间来审查这段代码,但由于我对Common Lisp的非常原始的了解,我似乎无法找到罪魁祸首.
提前致谢!
你错过了一个关键点:
现在你只需要包含
make-comparison-list
一个AND
和一个匿名函数返回的列表,你可以在where
宏本身做.使用后引号来制作一个通过插值来填充的模板make-comparisons-list
,这是微不足道的.Run Code Online (Sandbox Code Playgroud)(defmacro where (&rest clauses) `#'(lambda (cd) (and ,@(make-comparisons-list clauses))))
在这个版本中where
是一个宏,而不是一个函数; 即你需要使用defmacro
而不是使用它来定义它defun
.
问题很容易找到.如果你知道在哪里看.;-)
错误表明这#'(LAMBDA (CD) (AND (EQUAL (GETF CD :ARTIST) "Dixie Chicks")))
不是函数或符号.
它是什么?这是一个清单.
CL-USER 9 > '#'(LAMBDA (CD) (AND (EQUAL (GETF CD :ARTIST) "Dixie Chicks")))
(FUNCTION (LAMBDA (CD) (AND (EQUAL (GETF CD :ARTIST) "Dixie Chicks"))))
CL-USER 10 > (type-of *)
CONS
Run Code Online (Sandbox Code Playgroud)
但是a cons
不是函数对象或符号.
那么你只需要找到为什么你有这个列表而不是一个函数对象.然后,您可以看到where
返回列表.如果where
是宏,它将创建此列表作为源,然后将其计算为函数对象.