Peter Seibel의 Practical Common Lisp의 MP3 데이터베이스 예제를 통해 작업하고 있습니다. Seibel은 매크로를 사용하여 where
함수의 코드를 단축하는 방법을 보여줍니다. 이제는 매크로를 사용하여 update
함수의 코드를 줄이려고합니다. 합니다 (update
함수의 원래 버전은 참조를 위해 포함되어 있습니다.) 내 코드를 실행하면 다음과 같은 오류가 마지막에서 두 번째 줄에서 유래 -왜 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