Я недавно начал кодировать на Лиспе, и уже очень впечатлен макросами - они позволили мне делать сложные разворачивание цикла во время компиляции, что-то, что я не могу сделать элегантно на любом другом языке, о котором я знаю (т.е. генерировать код с сохранением исходной структуры).
По поводу оптимизации: я добавил аннотации типов (много «фиксированного числа») в один и тот же код. Как только я добавил 3 или 4 из них, я понял, что делаю это неправильно - для этого нужны макросы, Dont Repeat Yourself ...
; whenever we want to indicate that the result of an operation
; fits in a fixnum, we macro expand (the fixnum (...))
(defmacro fast (&rest args)
`(the fixnum ,args))
...
(cond
(...)
(t (let* ((forOrange (+ (aref counts 5)
(fast * 2 (aref counts 6))
(fast * 5 (aref counts 7))
(fast * 10 (aref counts 8))))
(forYellow (+ (aref counts 3)
(fast * 2 (aref counts 2))
(fast * 5 (aref counts 1))
(fast * 10 (aref counts 0))))
... и действительно, это сработало: вместо того, чтобы писать много " (fixnum (...)) "везде, я просто быстро ставлю перед выражением префикс" быстро "- и все хорошо.
Но потом ...
Я понял, что даже на этом нельзя останавливаться: в принципе, макрос "fast" должен ... вызываться в начале оценки, в данном случае:
(forYellow (fast + (aref counts 3)
(* 2 (aref counts 2))
(* 5 (aref counts 1))
(* 10 (aref counts 0))))
... и он должен рекурсивно "сажать" "(fixnum (...))" во всех подвыражениях.
Можно ли это сделать? Может ли "дефмакро" быть рекурсивным?
ОБНОВЛЕНИЕ : Я столкнулся с некоторыми действительно странными проблемами, пытаясь сделать это, поэтому в итоге я сделал то, что Рорд предложил ниже - т.е. реализовал функцию, протестировал ее в ответе и вызвал ее из макроса:
(defun operation-p (x)
(or (equal x '+) (equal x '-) (equal x '*) (equal x '/)))
(defun clone (sexpr)
(cond
((listp sexpr)
(if (null sexpr)
()
(let ((hd (car sexpr))
(tl (cdr sexpr)))
(cond
((listp hd) (append (list (clone hd)) (clone tl)))
((operation-p hd) (list 'the 'fixnum (cons hd (clone tl))))
(t (cons hd (clone tl)))))))
(t sexpr)))
(defmacro fast (&rest sexpr)
`(,@(clone sexpr)))
И она отлично работает под SBCL:
$ sbcl
This is SBCL 1.0.52, an implementation of ANSI Common Lisp.
...
* (load "score4.cl")
T
* (setf a '(+ (1 2) (- 1 (+ 5 6)))
...
* (clone a)
(THE FIXNUM (+ (1 2) (THE FIXNUM (- 1 (THE FIXNUM (+ 5 6))))))
* (macroexpand '(fast + 1 2 THE FIXNUM (- 1 THE FIXNUM (+ 5 6))))
(THE FIXNUM (+ 1 2 THE FIXNUM (THE FIXNUM (- 1 THE FIXNUM (THE FIXNUM (+ 5 6))))))
T
Все хорошо, за исключением одного побочного эффекта: CMUCL работает, но больше не компилирует код:
; Error: (during macroexpansion)
; Error in KERNEL:%COERCE-TO-FUNCTION: the function CLONE is undefined.
Ну что ж: -)
ОБНОВЛЕНИЕ : Ошибка компиляции была устранена и решена в другом вопросе SO .