Common Lisp:将宏生成的lambda传递给remove-if-not会导致错误

Gra*_*oom 1 common-lisp

当我在本书提供的最终产品中遇到错误时,我正在阅读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的非常原始的了解,我似乎无法找到罪魁祸首.

提前致谢!

mel*_*ene 5

你错过了一个关键点:

现在你只需要包含make-comparison-list一个AND和一个匿名函数返回的列表,你可以在where宏本身做.使用后引号来制作一个通过插值来填充的模板make-comparisons-list,这是微不足道的.

(defmacro where (&rest clauses)
  `#'(lambda (cd) (and ,@(make-comparisons-list clauses))))
Run Code Online (Sandbox Code Playgroud)

在这个版本中where是一个宏,而不是一个函数; 即你需要使用defmacro而不是使用它来定义它defun.


Rai*_*wig 5

问题很容易找到.如果你知道在哪里看.;-)

错误表明这#'(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是宏,它将创建此列表作为源,然后将其计算为函数对象.

  • 现在这是一个很好的“教他们钓鱼”的答案!所以需要一个“元答案”复选标记。 (2认同)