Эта форма:
(defmacro test (lst)
(dolist (x lst)
(print x)))
определяет макрос , который является «функцией преобразования кода», которая применяется к форме, использующей этот макрос во время раскрытия макроса.Итак, после того, как вы определили этот макрос, когда вы вычисляете это выражение:
(test (1 2 3))
, оно сначала читается в этот список:
(test (1 2 3))
Затем, поскольку Лисп читает test
впозиция оператора, он раскрывается макросом, передавая аргумент, который является буквенным списком (1
2 3)
, в функцию раскрытия макросов, определенную выше.Это означает, что во время макроразложения оценивается следующее:
(dolist (x '(1 2 3))
(print x))
Итак, во время макроразложения печатаются три значения.Наконец, возвращаемое значение этой формы возвращается как код для компиляции и выполнения.Dolist
возвращает nil
здесь, так что это возвращаемый код:
nil
Nil
оценивается как nil
, что возвращается.
Как правило, такой макросне очень полезноСм. «Практический общий Лисп» Питера Сейбела или «На Лиспе» Пола Грэма для ознакомления с полезными макросами.
Обновление: Возможно, будет полезно пересмотреть порядокчтение, расширение и оценка кода на Лиспе.
Сначала REPL принимает поток символов: (
t
e
s
t
(
1
2
3
)
)
, которые он собирает в токены: (
test
(
1
2
3
)
)
.
Затем это переводится в дерево символов: (test
(1
2
3
)).На этом этапе могут быть задействованы так называемые макросы чтения .Например, 'x
переводится в (quote
x
).
Затем, снаружи извне, проверяется каждый символ в позиции оператора (т. Е. Первая позиция в форме).Если он называет макрос, то соответствующая функция макроса вызывается с кодом (т. Е. Поддеревьями символов), который является остальной частью формы в качестве аргументов.Предполагается, что функция макроса возвращает новую форму, то есть code , которая заменяет форму макроса.В вашем случае макрос test
получает код (1
2
3
) в качестве аргумента, печатает каждый из символов, содержащихся в нем (обратите внимание, что это еще до времени компиляции), и возвращает nil
,выбрасывая аргументы (компилятор даже не видит ваш маленький список)Затем возвращенный код снова проверяется на предмет возможных расширений макросов.
Наконец, расширенный код, который больше не содержит вызовов макросов, оценивается , то есть компилируется и выполняется.Nil
оказывается самооценочным символом;он оценивается как nil
.
Это всего лишь грубый набросок, но я надеюсь, что он прояснит некоторые вещи.