Ваш код с лучшим отступом выглядит следующим образом:
(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
находится в неправильной позиции, и ожидается, что это список с формой окончания теста в качестве первого элемента:
(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
Лисп ожидал список с тестом в первой позиции и формой результата:
(cond ; the test is just a variable reference
((NULL L) result) ; result form number one
...)
Теперь тест по-прежнему выглядит действительным, но первая форма результата не является:
((NULL L) result)
Список в качестве первого элемента формы недопустим в Common Lisp - за одним исключением: лямбда-выражение:
((lambda (a b) (+ a b 10)) 12 20)
Таким образом, ваша реализация на Лиспе жалуется, что (null l)
не является лямбда-выражением , Здесь я думаю, что сообщения об ошибках могут быть улучшены в реализациях ...
Дополнительные отзывы
Существует дополнительная проблема: 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)))))
Следующее улучшение - это stylisti c: код в основном пишется в нижнем регистре и подчеркивания не используются. Вместо этого используются дефисы:
(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)))))
Следующим улучшением является стилист c: car
и cdr
являются старомодными и используются для операций с ячейками "против". Если мы имеем дело со списками, мы используем 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))))
Правила стиля
Повторение правил стиля сверху:
- ищите синтаксис специальных операторов, используя HyperSpe c (или аналогичный)
- , корректный отступ кода, редактор должен сделать это для вас
- определить ваши переменные
- глобальные переменные записываются как
*variable-name*
- писать в основном строчными буквами
- использовать дефисы в составных символах между слова:
retrieve-by
- часто существуют функции или другие операторы, которые проще в использовании, чем оператор
do
- , не используйте висячие скобки
- пишите компактный код без лишних пробельных строк