#macros #lisp
#макросы #lisp
Вопрос:
Мои первые шаги с макросами Lisp…
(defconstant width 7)
(defconstant height 6)
...
; board is a 2D array of width x height
; and this is my first ever macro:
(defmacro at (y x)
`(aref board ,y ,x))
; "board" must be available wherever the macro is used.
(defun foo (board ...)
...
(loop for y from 0 to (1- height) do
; thanks to the "at" macro, this is cleaner:
(let ((score ( (at y 0) (at y 1) (at y 2))))
(loop for x from 3 to (1- width) do
(incf score (at y x))
; ...do something with score
(decf score (at y (- x 3)))))))
В коде используется мой первый в истории макрос, «at». Он выдает «инструкции доступа» для чтения с платы [y] [x], поэтому его можно использовать только в тех местах, где существует «плата», как функция «foo» выше.
Это сработало — и тогда я понял это… Я могу пойти дальше.
Два вложенных цикла «статически» ограничены: от 0 до высоты-1 для y, от 3 до (ширина-1) для x … так что теоретически я могу создать макрос, который выдает (разворачивает!) точные инструкции incf и decf, выполняемые в коде циклов!
Я пробовал это:
(defmacro unroll ()
(loop for y from 0 to (1- height) do
`(setf score ( (at ,y 0) (at ,y 1) (at ,y 2)))
(loop for x from 3 to (1- width) do
`(incf score (at ,y ,x))
`(decf score (at ,y (- ,x 3))))))
…но не удалось — «(macroexpand-1 ‘(развернуть))» показывает мне НОЛЬ.
Что я делаю не так?
В случае, если это неясно, я хочу использовать два вложенных цикла и выдавать «код» в начале внешнего цикла и для каждой итерации внутреннего цикла.
Любая помощь наиболее ценится (я новичок в LISP).
ОБНОВЛЕНИЕ: После доброго совета @larsmans мне удалось применить это изменение к своему коду — и, к моему огромному удовлетворению, я наблюдал, как версия моего алгоритма Score4 на Lisp стала 2-й по быстродействию реализацией, уступая только C и C (и быстрее, чем OCaml!).
Ответ №1:
Вы должны collect
генерировать инструкции внутри макроса loop
, а не притворяться, что выполняете их с do
:
(defmacro unroll ()
(loop for y from 0 to (1- height)
collect
`(begin (setf score ( (at ,y 0) (at ,y 1) (at ,y 2)))
,@(loop for x from 3 to (1- width)
collect `(begin (incf score (at ,y ,x))
(decf score (at ,y (- ,x 3))))))))