我看了Practical Common Lisp中的第三章。在那一章中,我们创建了一个类似于应用程我坚持理解update
函数。
我已经在我的编辑器中编写了代码并将注释放入我自己对代码的理解中:
(defun update (selector-fn &key title artist rating (ripped NIL ripped-p))
(setf ; set ...
*db* ; *DB* to ...
(mapcar ; the value of the application of ...
#'(lambda (row) ; a lambda to rows ...
(when (funcall selector-fn row) ; when a row satisfies ...
; (what does funcall do? if I call the selector function
; why do I still have to check for predicates as below?)
; maybe "when a row is selected"? Is WHEN like a loop over rows?
(if title (setf (getf row :title) title)) ; the title predicate ...
(if artist (setf (getf row :artist) artist)) ; the artist predicate ...
(if rating (setf (getf row :rating) rating)) ; the rating predicate ...
(if ripped-p (setf (getf row :ripped) ripped))) ; and the ripped predicate ...
; why cannot we use our selector function here instead of repeating stuff?!
row) ; why is there a ROW here? isn't the lambda expression already finished?
; maybe the WHEN expression does not return anything and this is a return value of the lambda?
*db*))) ; applies the lambda to the database
以前提供了where
功能:
(defun where (&key title artist rating (ripped NIL ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) T)
(if artist (equal (getf cd :artist) artist) T)
(if rating (equal (getf cd :rating) rating) T)
(if ripped-p (equal (getf cd :ripped) ripped) T))))
正如您所看到的,本书中提供的代码存在一些问题。我将再次在下面列出它们,但请留下评论,以便更清楚,它们与之相关。
where
函数,而不是再次编写所有if
表达式?funcall
(该章中没有对该代码进行解释......)真正调用where
函数调用的选择函数(返回值)给定,为什么我必须在那里写出所有if
个表达式?这不就是where
函数返回的那个吗?这些行的选择器符合标准吗?row
表达式之后有一个when
,它似乎属于lambda
表达式?这是一个返回值,因为when
表达式没有返回任何内容,所以lambda
返回更新的行?我觉得这个代码没有正确解释一些相当高级的语法,我只是猜测代码是如何工作的。
对代码的示例调用将是:
(update (where :artist "artist1") :rating 11)
我试过了,它确实奏效了。
这是我的“数据库”:
((:TITLE "title3" :ARTIST "artist1" :RATING 10 :RIPPED T)
(:TITLE "title2" :ARTIST "artist2" :RATING 9 :RIPPED T)
(:TITLE "title1" :ARTIST "artist1" :RATING 8 :RIPPED T))
到目前为止,这是完整的代码:
(getf (list :a 1 :b 2 :c 3) :b)
(defvar *db* nil)
(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) ; this is a list as parameter! not a function call
; OUT holds the output stream
; opening a file for writing with :DIRECTION :OUTPUT
; if it already exists overrite it :IF-EXISTS :SUPERSEDE
(with-standard-io-syntax (print *db* out))
; The macro WITH-STANDARD-IO-SYNTAX ensures that certain variables
; that affect the behavior of PRINT are set to their standard values.
))
(defun load-db (filename)
(with-open-file
(in filename)
; IN contains the input stream
(with-standard-io-syntax (setf *db* (read in)))
; file contains standard syntax of lisp
; SETF sets the value of *DB* to what is read from IN
; WITH-STANDARD-IO-SYNTAX macro ensures that READ is using the same basic
; syntax that save-db did when it PRINTed the data.
))
(defun select-by-artist (artist)
(remove-if-not
#'(lambda (cd) (equal (getf cd :artist) artist))
*db*))
(defun select (selector-fn)
(remove-if-not selector-fn *db*))
(load-db "database")
(dump-db)
; not so general selector function
(defun artist-selector (artist)
#'(lambda (cd) (equal (getf cd :artist) artist)))
; "general" selector function
(defun where (&key title artist rating (ripped NIL ripped-p))
#'(lambda (cd)
(and
(if title (equal (getf cd :title) title) T)
(if artist (equal (getf cd :artist) artist) T)
(if rating (equal (getf cd :rating) rating) T)
(if ripped-p (equal (getf cd :ripped) ripped) T))))
(print (select (where :artist "artist1")))
(defun update (selector-fn &key title artist rating (ripped NIL ripped-p))
(setf ; set ...
*db* ; *DB* to ...
(mapcar ; the value of the application of ...
#'(lambda (row) ; a lambda to rows ...
(when (funcall selector-fn row) ; when a row satisfies ...
; (what does funcall do? if I call the selector function
; why do I still have to check for predicates as below?)
; maybe "when a row is selected"? Is WHEN like a loop over rows?
(if title (setf (getf row :title) title)) ; the title predicate ...
(if artist (setf (getf row :artist) artist)) ; the artist predicate ...
(if rating (setf (getf row :rating) rating)) ; the rating predicate ...
(if ripped-p (setf (getf row :ripped) ripped))) ; and the ripped predicate ...
; why cannot we use our selector function here instead of repeating stuff?!
row) ; why is there a ROW here? isn't the lambda expression already finished?
; maybe the WHEN expression does not return anything and this is a return value of the lambda?
*db*))) ; applies the lambda to the database
答案 0 :(得分:4)
where
函数通过评估(and
)不同的行来执行元素的类似SQL的“选择”:
(if title (equal (getf cd :title) title) T)
...
查找某个“字段”是否具有指定为该函数参数的值。例如,您可以使用(where :rating 10 :ripped nil)
调用它,如文中所述。
update
函数改为执行一个或多个字段的类似SQL的“更新”。你应该注意到,匿名内部函数的主体与where
函数完全不同,因为它有如下行:
(if title (setf (getf row :title) title))
...
这些是更新“字段”所需的行,而不是测试它们。实际上,他们使用的是setf
,而不是equal
。因此,如果我们使用通用SQL查询绘制并行,when
函数对应于SQL WHERE
之后的部分:
SELECT *
FROM CDs
WHERE field1 = value1
AND field2 = value2,
...
同时,在update
函数中,对(funcall selector-fn row)
部分的调用与WHERE
部分相对应,而行(if ... (setf ...))
对应于SET
部分:
UPDATE CDs
SET field1 = value1,
field2 = value2,
...
WHERE field1 = value1
AND field2 = value2,
...
例如,调用如:
(update (where :artist "artist1") :rating 11)
等同于SQL查询:
UPDATE CDs
SET rating = 11
WHERE artist = 'artist1'
那么,update
中的评论内容被称为; the title predicate
等等,真的应该是; the setting of the title
等等。
所以,你的问题的答案是:
没有代码重复,两组行执行两个不同的任务:在where
中,它们用于使用equal
过滤元素,在update
中它们是(funcall f args)
用于设置具有新值的字段。
f
将函数args
应用于参数where
。因此,为每一行调用选择器update
,以查看它是否满足仅过滤必须修改的行 的谓词。
when
内的匿名函数以这种方式工作:首先,如果满足setf
内的条件,则通过row
执行赋值来更新行。最后,它会返回selector-fn
,如果*db*
已返回 true 或 false ,则可以修改data_transformer.cpp:239] Check failed: channels == img_channels (3 vs. 1)
。因此,更新函数使用该匿名函数返回的值更新channels
。