Common lisp Common Lisp:传递宏生成的lambda以删除,如果不是,则会导致错误

Common lisp Common Lisp:传递宏生成的lambda以删除,如果不是,则会导致错误,common-lisp,Common Lisp,当我在书中提供的最终产品中遇到错误时,我正在跟随书中的第一个示例项目 该项目是一个基本的数据库,存储有关CD的信息。它支持select和update语句与where宏一起使用 我已经使用clisp和sbcl编译器测试了代码,两个编译器都出现了相同的错误 代码如下: (defvar *db*) (defun make-cd (title artist rating ripped) (list :title title :artist artist :rating rating :ripped

当我在书中提供的最终产品中遇到错误时,我正在跟随书中的第一个示例项目

该项目是一个基本的数据库,存储有关CD的信息。它支持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*)))
以下是产生错误的调用:

(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))
以下是上述调用的预期结果:

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))
在寻求帮助之前,我花了大量的时间来检查这段代码,但是由于我对CommonLisp非常原始的了解,我似乎找不到罪魁祸首


提前谢谢

您错过了一个关键点:

现在,您只需将
make comparison list
返回的列表包装到
以及匿名函数中,您可以在
where
宏本身中执行此操作。使用后引号生成一个模板,您可以通过插入
生成比较列表的值来填充该模板,这很简单

(defmacro where (&rest clauses)
  `#'(lambda (cd) (and ,@(make-comparisons-list clauses))))

在这个版本中,
其中
是宏,而不是函数;i、 e.您需要使用
defmacro
来定义它,而不是
defun

您错过了一个关键点:

现在,您只需将
make comparison list
返回的列表包装到
以及匿名函数中,您可以在
where
宏本身中执行此操作。使用后引号生成一个模板,您可以通过插入
生成比较列表的值来填充该模板,这很简单

(defmacro where (&rest clauses)
  `#'(lambda (cd) (and ,@(make-comparisons-list clauses))))

在这个版本中,
是一个宏,而不是一个函数;i、 e.您需要使用
defmacro
来定义它,而不是
defun

问题很容易找到。如果你知道去哪里找

错误表明
#'(LAMBDA(CD)(和(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
但是
cons
不是函数对象或符号


所以你只需要找到为什么你得到这个列表而不是一个函数对象。然后可以看到,
其中
返回列表。如果
中的
是一个宏,它将创建此列表作为源,然后将其计算为函数对象。

问题很容易找到。如果你知道去哪里找

错误表明
#'(LAMBDA(CD)(和(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
但是
cons
不是函数对象或符号


所以你只需要找到为什么你得到这个列表而不是一个函数对象。然后可以看到,
其中
返回列表。如果
where
是一个宏,它将创建这个列表作为源,然后将其计算为一个函数对象。

where
应该是一个
defmacro
,而不是
defun
(不要忘记在更改后重新编译
where
的所有用户)。@sds非常感谢您。我发誓我盯着这个看了两个小时。哎呀。
where
应该是
defmacro
,而不是
defun
(更改后不要忘记重新编译
where
的所有用户)。@sds非常感谢。我发誓我盯着这个看了两个小时。哦,这是一个很好的“教他们钓鱼”的答案!所以需要一个“元答案”复选标记。现在这是一个很好的“教他们钓鱼”答案!所以需要一个“元答案”复选标记。