Проблема с поведением макросов в lisp - PullRequest
3 голосов
/ 23 февраля 2011

Если в REPL я делаю это:

(dolist (x (1 2 3))
  (print x))

тогда я получаю ошибку, поскольку в (1 2 3) цифра 1 не является символом или лямбда-выражением. Если я сделаю:

(dolist (x (list 1 2 3))
      (print x))

тогда все работает нормально.

У меня вопрос, почему работает следующее:

REPL> (defmacro test (lst)
           (dolist (x lst)
             (print x)))
=> TEST
REPL> (test (1 2 3))
1
2
3
=>NIL

Почему dolist принимает (1 2 3), когда он находится внутри определения макроса, а не когда непосредственно в репле? Предположение:

"Поскольку TEST является макросом, он не оценивает свои аргументы, поэтому (1 2 3) передается как есть в макрос dolist. Поэтому dolist должен жаловаться так же, как и при передаче (1 2 3) в REPL "

явно не так. Но где?

ОБНОВЛЕНИЕ: Хотя ответы помогают прояснить некоторые недоразумения с макросами, мой вопрос все еще остается, и я попытаюсь объяснить, почему:

Мы установили, что dolist оценивает свой аргумент списка (кодовые блоки 1, 2). Что ж, похоже, это не тот случай, когда он вызывается внутри определения макроса, и переданный ему аргумент списка является одним из определенных макро-аргументов (блок кода 3). Больше деталей: При вызове макрос не оценивает свои аргументы. Поэтому мой тестовый макрос, когда он вызывается, сохранит аргумент списка и передаст его как есть во время расширения. Затем во время расширения будет выполнен dolist (в моем тестовом макросе def нет обратных кавычек). И он будет выполнен с (1 2 3) в качестве аргумента, поскольку это то, что ему передал тестовый макрос. Так почему же он не выдает ошибку, так как dolist пытается оценить свой аргумент списка, и в этом случае его аргумент списка (1 2 3) не может быть оценен. Надеюсь, это немного прояснит мою путаницу.

Ответы [ 5 ]

5 голосов
/ 23 февраля 2011

Эта форма:

(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.

Это всего лишь грубый набросок, но я надеюсь, что он прояснит некоторые вещи.

4 голосов
/ 23 февраля 2011

Как правило, когда у вас есть вопросы о расширении макросов, ссылка на MACROEXPAND-1 - отличный первый шаг.

* (macroexpand-1 '(test (1 2 3)))

1 
2 
3 
NIL
T

IE, то, что происходит, это то, что расширение фактическое таково, чтопоследовательность отпечатков.

Ноль - это то, что возвращается DOLIST, и является расширенным кодом.

4 голосов
/ 23 февраля 2011

Ваш макрос test не возвращает никакого кода. И да, аргументы макроса не оцениваются. Если вы хотите увидеть ту же ошибку, вы должны определить свой макрос как:

(тест defmacro (lst) `(dolist (x, lst) (печать х)))

1 голос
/ 28 февраля 2011

Макросы получают свои аргументы без оценки.Они могут выбрать, чтобы оценить их.dolist делает это для аргумента списка.Он работает со списком без кавычек, переданным для lst в вашем макросе test:

(defmacro test (lst)
  (dolist (x lst)
    (print x)))

Это потому, что во время расширения макроса dolist видит lst в качестве аргумента.Поэтому, когда он оценивает его, он получает список (1 2 3).

0 голосов
/ 19 мая 2012

lst является переменной, при расширении макроса теста, это также означает структуру eval dolistпервый шаг - оценить форму lst, получит объект lisp (1 2 3).

как в следующем примере:

(defmacro test (a) (+ a 2))

(тест 2) -> 4;означают, вызывают функцию add, а первая переменная связывает значение 2.

...