Common lisp 为什么lisp说这个参数不是列表?

Common lisp 为什么lisp说这个参数不是列表?,common-lisp,practical-common-lisp,Common Lisp,Practical Common Lisp,我正在学习Peter Seibel的实用公共Lisp中的MP3数据库示例。Seibel演示了如何使用宏来缩短where函数的代码;现在,我尝试使用宏来缩短update函数的代码。(包含update函数的原始版本以供参考。)当我运行代码时,以下错误源自第二行到最后一行-- 我做错了什么?这是我的密码 (defvar *db* nil) (defun add-record (cd) (push cd *db*)) (defun dump-db () (dolist (cd *db*)

我正在学习Peter Seibel的实用公共Lisp中的MP3数据库示例。Seibel演示了如何使用宏来缩短
where
函数的代码;现在,我尝试使用宏来缩短
update
函数的代码。(包含
update
函数的原始版本以供参考。)当我运行代码时,以下错误源自第二行到最后一行--

我做错了什么?这是我的密码

(defvar *db* nil)
(defun add-record (cd) 
  (push cd *db*))

(defun dump-db ()
  (dolist (cd *db*)
    (format t "~{~a:~10t~a~%~}~%" cd)))

(defun make-cd (title artist rating ripped)
  (list :title title :artist artist :rating rating :ripped ripped))

(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-comparison-list (func fields)
  (loop while fields
        collecting (funcall func (pop fields) (pop fields))))

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

(defun make-update-expr (field value)
  `(setf (getf row ,field) ,value))

(defmacro make-update-list (fields)
  (make-comparison-list 'make-update-expr fields))

(defun update (selector-fn &rest terms)
  (print (type-of terms))
  (setf *db*
        (mapcar
          #'(lambda (row)
             (when (funcall selector-fn row)
               (make-update-list terms))
             row)
          *db*)))

;(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 delete-rows (selector-fn)
  (setf *db* (remove-if selector-fn *db*)))

;(loop (print (eval (read))))
(add-record (make-cd "Be" "Common" 9 nil))
(add-record (make-cd "Like Water for Chocolate" "Common" 9 nil))
(add-record (make-cd "Be" "Beatles" 9 nil))

(dump-db)
(update (where :artist "Common" :title "Be") :rating 8)
(dump-db)
-----编辑-----

我想出来了。解决方案是使
update
成为一个宏,并使
makeupdatelist
成为一个函数。这样,
makeupdatelist
可以在运行时对字段求值,
update
仍然可以抽象出一些繁琐的if语句。下面是更新的
更新
制作更新列表

(defun make-update-list (fields)
  (make-comparison-list 'make-update-expr fields))

(defmacro update (selector-fn &rest terms)
  `(setf *db*
        (mapcar
          #'(lambda (row)
             (when (funcall ,selector-fn row)
               ,@(make-update-list terms))
             row)
          *db*)))

生成更新列表的宏扩展在单独的阶段(称为“宏扩展阶段”)中完成,该阶段在编译或加载代码时发生;在本例中,我们讨论的是
update
的编译/加载。宏通过绑定到符号
术语的
字段展开,该符号(符号本身)用作
生成比较列表中的值;我想那不是你所期望的

注意,如果您在Emacs+SLIME中逐行编译文件(
C-C-C
),它会在编译
update
期间告诉您宏扩展失败,因为“值项不是列表类型”


通常,将宏视为接受未赋值参数的函数,即表单
(生成更新列表foo)
将通过绑定到
foo
的宏参数的
字段
值展开。您在这里试图实现的是基于运行时值的代码生成,这一点要困难一些。

生成更新列表的宏扩展是在一个单独的阶段(称为“宏扩展阶段”)中完成的,该阶段发生在编译或加载一段代码时;在本例中,我们讨论的是
update
的编译/加载。宏通过绑定到符号
术语的
字段展开,该符号(符号本身)用作
生成比较列表中的值;我想那不是你所期望的

注意,如果您在Emacs+SLIME中逐行编译文件(
C-C-C
),它会在编译
update
期间告诉您宏扩展失败,因为“值项不是列表类型”


通常,将宏视为接受未赋值参数的函数,即表单
(生成更新列表foo)
将通过绑定到
foo
的宏参数的
字段
值展开。在这里,您试图实现的是基于运行时值的代码生成,这一点要困难一些。

您正在尝试乘坐符号的
汽车

> (car 'terms)
*** - CAR: TERMS is not a list
将宏视为一个函数,当使用宏函数时,它会在使用宏函数的任何地方用宏函数的结果替换代码。此时变量只是符号,除此之外没有任何意义

当您执行
(生成更新列表术语)
时,它将调用宏函数,参数
字段
是您传递的符号,即
术语
。因为它是一个符号,所以不能像您尝试的那样进行迭代。当它确实是一个列表时,您可以在运行时对其进行迭代,但作为一个宏,它不是一个列表,除非您向它传递一个类似于
(makeupdatelist(title-artist-rating-ripped))
的列表


如果宏在运行时是动态的,那么您的宏需要扩展到在运行时发挥其大部分魔力的代码。因此,宏只是一个源代码重写服务,不应该与运行时的变量有任何关系,因为它已经完成了它的任务。

您正在尝试使用符号的
汽车

> (car 'terms)
*** - CAR: TERMS is not a list
将宏视为一个函数,当使用宏函数时,它会在使用宏函数的任何地方用宏函数的结果替换代码。此时变量只是符号,除此之外没有任何意义

当您执行
(生成更新列表术语)
时,它将调用宏函数,参数
字段
是您传递的符号,即
术语
。因为它是一个符号,所以不能像您尝试的那样进行迭代。当它确实是一个列表时,您可以在运行时对其进行迭代,但作为一个宏,它不是一个列表,除非您向它传递一个类似于
(makeupdatelist(title-artist-rating-ripped))
的列表


如果宏在运行时是动态的,那么您的宏需要扩展到在运行时发挥其大部分魔力的代码。因此,宏只是一个源代码重写服务,不应该与运行时的变量有任何关系,因为它已经完成了它的任务。

我希望您能对最后一句话进行一点扩展。这是不可能的,还是更难做到?我认为这是不可能的,因为编程语言的编译时和运行时之间通常有一个严格的界限。或者有办法吗?有点麻烦。您希望使用
eval
计算运行时生成的s表达式
eval
也执行宏扩展阶段(就像通过
macroexpand-1
)。更多信息请访问。我故意没有详述我评论的最后一部分,因为wrt可能有一些警告。我没有足够的经验来正确解释运行时的宏。也就是说,在运行时做你正在做的事情是没有意义的——代码生成的开销将比跳过简单的
if
s的开销要大。我希望你能把最后一句话扩展一下。这是不可能的,还是更难做到?我认为这是不可能的,因为