Ваш пример не работает.
Может работать в переводчике. Но с компилятором вы увидите бесконечный цикл во время компиляции.
CL-USER 23 > (defun test (foo)
(sum-int-seq 5))
TEST
Давайте использовать интерпретатор LispWorks:
CL-USER 24 > (test :foo)
15
Давайте попробуем скомпилировать функцию:
CL-USER 25 > (compile 'test)
Stack overflow (stack size 15997).
1 (continue) Extend stack by 50%.
2 Extend stack by 300%.
3 (abort) Return to level 0.
4 Return to top loop level 0.
Type :b for backtrace or :c <option number> to proceed.
Type :bug-form "<subject>" for a bug report template or :? for other options.
Итак, теперь следующий вопрос: почему он работает в интерпретаторе, но компилятор не может его скомпилировать?
Хорошо, я объясню.
Давайте сначала посмотрим на переводчика.
- видит
(sum-int-seq 5)
.
- макрос расширяет его до
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
.
- Затем он оценивает форму выше. Он определяет, что ему нужно вычислить
(+ 5 (SUM-INT-SEQ (- 5 1)))
. Для этого нужно макроэкспандировать (SUM-INT-SEQ (- 5 1))
.
- в конце концов он расширится до
(cond ((EQUAL 0 (- (- (- (- (- 5 1) 1) 1) 1) 1)) 0) ...
. Который затем вернет 0, и вычисление может использовать этот результат и добавить к нему другие условия.
Интерпретатор берет код, оценивает, что он может, и при необходимости расширяет макрос. Сгенерированный код затем оценивается или расширяется макросом. И так далее.
Теперь давайте посмотрим на компилятор.
- он видит (sum-int-seq 5) и макрос расширяет его до
(COND ((EQUAL 0 5) 0) (T (+ 5 (SUM-INT-SEQ (- 5 1)))))
.
- теперь макроразложение будет выполнено для подчиненных форм, в конце концов.
- компилятор будет макроэкспандировать
(SUM-INT-SEQ (- 5 1))
. обратите внимание, что код никогда не оценивается, только раскрывается.
- компилятор будет макроэкспандировать
(SUM-INT-SEQ (- (- 5 1) 1))
и так далее. наконец, вы увидите переполнение стека.
Компилятор просматривает (рекурсивно компилирует / расширяет) код. Он может не выполнять код (если он не выполняет оптимизацию или макрос не выполняет его явно).
Для рекурсивного макроса вам нужно будет вести обратный отсчет. Если вы используете eval внутри макроса, то что-то вроде (sum-int-seq 5)
может работать. Но для (defun foo (n) (sum-int-seq n))
это безнадежно, так как компилятор не знает, каково значение n.