您的代码缩进效果更好,如下所示:
(defun retrieve_by (property my_value)
(setq result nil)
(do ((L library (cdr L)))
(cond
((NULL L) result)
(equal (get (car L) property) my_value)
(cons (car L) result))))
do 的语法是:
do ({var | (var [init-form [step-form]])}*)
(end-test-form result-form*)
declaration*
{tag | statement}*
这提示我们您的 cond 位置错误,并且预期是一个以 end-test-form 作为其第一个元素的列表:
(defun retrieve_by (property my_value)
(setq result nil)
(do ((L library (cdr L))) ; iterating L
((NULL L) result) ; end test + result
(if (equal (get (car L) property) my_value)
(push (car L) result))))
您为什么会看到该错误消息?
"SYSTEM::%EXPAND-FORM: (NULL L) should be a lambda expression"
(do ((L library (cdr L))) ; iterating
(cond ((NULL L) result) ...) ; end-test + result forms
Lisp 期望在第一个位置有一个测试列表和结果形式:
(cond ; the test is just a variable reference
((NULL L) result) ; result form number one
...)
现在测试看起来仍然有效,但第一个结果表单无效:
((NULL L) result)
Common Lisp 中不允许将列表作为表单的第一个元素 - 有一个例外:lambda 表达式:
((lambda (a b) (+ a b 10)) 12 20)
因此,您的 Lisp 实现抱怨 (null l) 不是 lambda 表达式。在这里我认为错误消息可以在实现中改进......
更多反馈
还有一个问题:result 是一个未定义的变量。我们需要创建一个局部变量result。这是通过let 完成的:
(defun retrieve_by (property my_value)
(let ((result nil))
(do ((L library (cdr L)))
((NULL L) result)
(if (equal (get (car L) property) my_value)
(push (car L) result)))))
另一个问题:library 是一个全局变量。这些写成*library*,并由DEFPARAMETER或DEFVAR定义:
(defvar *library* nil)
(defun add_book (bookref title author publisher)
(setf (get bookref 'title) title)
(setf (get bookref 'author) author)
(setf (get bookref 'publisher) publisher)
(setq *library* (cons bookref *library*))
bookref)
(defun retrieve_by (property my_value)
(let ((result nil))
(do ((L *library* (cdr L)))
((null L) result)
(if (equal (get (car L) property) my_value)
(push (car L) result)))))
下一个改进是文体:代码大多以小写形式编写,不使用下划线。而是使用连字符:
(defvar *library* nil)
(defun add-book (bookref title author publisher)
(setf (get bookref 'title) title)
(setf (get bookref 'author) author)
(setf (get bookref 'publisher) publisher)
(setq *library* (cons bookref *library*))
bookref)
(defun retrieve-by (property my_value)
(let ((result nil))
(do ((list *library* (cdr list)))
((null lisp) result)
(if (equal (get (car list) property) my_value)
(push (car list) result)))))
下一个改进是风格:car 和 cdr 是老式的,用于 cons 单元操作。如果我们处理列表,我们使用first 和rest。
这是我们的代码:
(defvar *library* nil)
(defun add-book (bookref title author publisher)
(setf (get bookref 'title) title)
(setf (get bookref 'author) author)
(setf (get bookref 'publisher) publisher)
(push bookref *library*)
bookref)
(defun retrieve-by (property my-value)
(let ((result nil))
(do ((list *library* (rest list)))
((null list) result)
(if (equal (get (first list) property) my-value)
(push (first list) result)))))
我们可以试试:
CL-USER 151 > (add-book 'johann-holtrop "Johann Holtrop" "Rainald Goetz" "Suhrkamp")
JOHANN-HOLTROP
CL-USER 152 > (retrieve-by 'title "Johann Holtrop")
(JOHANN-HOLTROP)
简化代码
Common Lisp 有一种更简单的形式来遍历一个列表:dolist:
(defun retrieve-by (property my-value)
(let ((result nil))
(dolist (bookref *library* result)
(if (equal (get bookref property) my-value)
(push bookref result)))))
Common Lisp 还具有查找项目的功能。一个奇怪的选择是将remove 与:test-not 参数一起使用。我们通过查看我们提取的关键值来保留所有满足我们测试的项目:
(defun retrieve-by (property my-value)
(remove my-value *library*
:test-not #'equal
:key (lambda (bookref)
(get bookref property))))
样式规则
从上面重复样式规则:
- 使用HyperSpec(或类似名称)查找特殊运算符的语法
- 正确缩进你的代码,编辑器应该为你做这件事
- 定义变量
- 全局变量写成
*variable-name*
- 主要是小写
- 在单词之间的复合符号中使用连字符:
retrieve-by
- 通常有函数或其他运算符,比
do运算符更容易使用
- 不要使用悬空括号
- 编写没有太多空白行的紧凑代码