私はPeter SeibelのPractical Common LispでMP3データベースの例を調べています。 Seibelは、マクロを使用してwhere
関数のコードを短縮する方法を示します。今、私はupdate
関数のコードを短縮するためにマクロを使用しようとしています。 (update
機能のオリジナルバージョンは、参考のために含まれています。)私は自分のコードを実行すると、以下のエラーが最後から2番目の行から発信 -なぜlispはこのパラメータがリストではないと言っていますか?
*** - CAR: TERMS is not a list
私が間違っているのか?ここに私のコードです。
(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
をマクロにしてmake-update-list
を機能させることでした。このようにして、make-update-list
は実行時にフィールドを評価し、update
は退屈なif文を抽象化できます。ここで以下update
とmake-update-list
更新される:別の相で行われることmake-update-list
の
(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*)))
私はあなたが最後の文章を少し拡大できることを願っていました。それは不可能か、それとももっと難しいですか?私はそれが不可能だと思うでしょう。通常、プログラミング言語のコンパイル時と実行時の間には厳しい境界が存在するためです。または方法がありますか? – barinska
もう少し面倒です。実行時に構築したs式を評価するために 'eval'を使いたいとします。 'eval'も(' macroexpand-1'のように)マクロ展開段階を実行します。 http://clhs.lisp.se/Body/f_eval.htmで詳細を参照してください。私は意図的に私のコメントの最後の部分を展開していませんでした。なぜならいくつかの注意点があるからです。私は適切に説明するのに十分な経験はありません。つまり、実行時に行うことは意味をなさない。コード生成のオーバーヘッドは単純な 'if'をスキップするオーバーヘッドよりも大きくなる。 – TeMPOraL