#lisp #common-lisp #sbcl #defmacro
#шепелявый #common-лисп #sbcl #дефмакро
Вопрос:
Я прокладываю себе путь через Практический Common Lisp. Я добрался до примера, где вы определяете deftest
макрос, который работает как defun
с некоторыми дополнительными функциями. Это навело меня на мысль, что было бы неплохо иметь возможность добавить строку документа. Я обнаружил, что оба следующих варианта работают, но является ли один из них более правильным? Есть ли «правильный» способ добиться необязательного поведения, подобного docstring defun
?
(defmacro deftest (name parameters amp;body body)
(let ((docstring ""))
(when (stringp (car body)) (setf docstring (car body) body (cdr body)))
`(defun ,name ,parameters
,docstring
(let ((*test-name* (append *test-name* (list ',name))))
,@body))))
(defmacro deftest (name parameters amp;optional docstring amp;body body)
(when (not (stringp docstring)) (setf docstring ""))
`(defun ,name ,parameters
,docstring
(let ((*test-name* (append *test-name* (list ',name))))
,@body)))
Ответ №1:
В общем случае вы, вероятно, захотите проанализировать как возможную строку документа, так и любые объявления из тела функции, чтобы вы могли поместить объявления туда, где они принадлежат. Я использую что-то вроде этого:
(defun parse-body (body)
(multiple-value-bind (docstring decls/forms)
(if (stringp (first body))
(values (first body) (rest body))
(values nil body))
(loop for remainder on decls/forms
while (and (not (null remainder))
(consp (first remainder))
(eql (car (first remainder)) 'declare))
collect (first remainder) into decls
finally (return (values docstring decls remainder)))))
И тогда ваш deftest
будет
(defmacro deftest (name parameters amp;body body)
(multiple-value-bind (docstring decls forms) (parse-body body)
`(defun ,name ,parameters
,@(if docstring (list docstring) '())
,@decls
(let ((*test-name* (append *test-name* (list ',name))))
,@forms))))
Хотел бы я сказать, что у меня есть стандартная parse-body
функция, но у меня ее нет: я просто каждый раз пишу новую.
Комментарии:
1. Спасибо, это четкий ответ. Есть только одна часть, которая меня все еще смущает. Почему необходимо / предпочтительнее использовать
,@(list docstring)
вместо,docstring
alone?2. @bashfuloctopus Смотри также common-lisp.net/project/parse-declarations/manual/html_node /…
3. @bashfuloctopus: извините, это была ошибка: у меня была рабочая версия, а потом я подумал: «О, нет, я могу быть умным и избежать условного выражения» (чего я не могу), что привело к неработающей версии, которую я никогда не тестировал. Трюк, который он теперь делает правильно, заключается в том, чтобы использовать
,@
для сращивания в пустом списке (т. Е. Ничего, поскольку он сращивается), если нет строки документа, в противном случае для сращивания в одноэлементном списке строки документа, что означает ввод строки документа. Извините за это.4. @tfb хитрый! Я хакерски предоставлял функции пустую строку документа, а не объединял пустой список.
5. @coredump
parse-body
выглядит удобно, но я не уверен, как получить к нему доступ. Я не вижу ссылки для скачивания, и я не смог загрузить ее с помощью(asdf:load-system 'parse-declarations)
. Что я упускаю?