2016-12-05 5 views
0

defmacro документально подтверждено на http://clhs.lisp.se/Body/m_defmac.htm, но документация не совсем понятна, когда это происходит. К эксперименту с CLISP, я обнаружил следующее (предполагая, что все макросы и функции, определенные на верхнем уровне):Правила, регулирующие порядок макрорасширения в Common Lisp

  • Прямой код верхнего уровня может вызвать только макросы и функции, которые были определены ранее.

  • Код в макросе или функции, или сгенерированный макросом, может вызывать любую функцию, которая ему нравится, в том числе определить позже (как ожидается, из-за необходимости поддержки взаимной рекурсии).

  • Код внутри макроса может вызывать только макрос, определенный раньше, чем вызывающий сайт первого макроса.

  • Код, сгенерированный макросом, может вызвать макрос, определенный позже.

Это тот случай, когда Clisp просто соответствует спецификации или существует ли какая-либо разница между реализациями в этом отношении?

Является ли конкретный намеченный набор правил и обоснование позади них документированным где угодно?

+1

http://clhs.lisp.se/Body/03_abab.htm –

+2

Подробное описание макросов см. По адресу: http://www.paulgraham.com/onlisp.html Вы можете скачать книгу оттуда. –

ответ

2

Вы спрашиваете о расширении макроса - но я хотел бы уточнить, как обрабатываются функции в первую очередь.

Обратите внимание, когда на самом деле происходят вызовы и определения. В вашей второй точке вы говорите, что код внутри функции может вызывать функцию, определенную позже. Это не совсем верно.

В таких языках, как C++, вы объявляете и определяете функции, а затем компилируете свое приложение. Игнорируя inlining, templates, lambdas и другую магию ... при компиляции функции должны присутствовать объявления всех других функций, используемых этой функцией, - и во время соединения должны быть скомпилированные определения - все перед программой начинает работать. После запуска программы все функции уже полностью подготовлены и готовы к вызову.

Теперь, в Лиспе, все по-другому. Игнорировать компиляцию пока - давайте просто подумаем о интерпретируемой среде. Если вы запустите:

;; time 1 
    (defun a() (b)) 
;; time 2 
    (defun b() 123) 
;; time 3 
    (a) 

В момент времени 1 ваша программа не имеет функций.

Первый defun затем создает функцию (lambda() (b)) и ассоциирует ее с символом a. Эта функция содержит ссылку на символ b, но на данный момент времени не звонитb. a позвонит по номеру b, когда вызывается a.

Итак, во время 2 ваша программа имеет одну функцию, ассоциированную с символом a, но пока не выполнена.

Теперь вторая defun создает функцию (lambda() 123) и связывает ее символом b.

В момент времени 3 ваша программа имеет две функции, связанные с символами a и b, но ни одна из них еще не была вызвана.

Теперь вы звоните a. Во время его выполнения он ищет функцию, связанную с символом b, , находит, что такая функция уже существует в этот момент времени и вызывает ее. b Выполняется и возвращается 123.

Давайте добавим еще код: ;; время defun b() 456) ;; время 5 (a)

После 4-го дня новый defun создает функцию, возвращающую 456, и связывает ее с символом b. Это заменяет ссылку b, удерживающую функцию, возвращающую 123, которая затем будет собираться мусором (или что бы вы ни делали, чтобы вынуть мусор).

Вызов a (или более правильно, лямбда, на который ссылается атрибут функции символа a), теперь приведет к вызову функции, которая возвращает 456.

Если, вместо этого, мы первоначально написал:

;; time 1 
    (defun a() (b)) 
;; time 2 
    (a) 
;; time 3 
    (defun b() 123) 

... это было бы не работали, потому что после времени 2, когда мы называем a, он не может найти функцию, связанную с символом b и поэтому она не будет выполнена.

В настоящее время - compile, eval-when, оптимизация и другая магия могут выполнять всевозможные надуманные вещи, отличные от того, что я описал выше, но сначала убедитесь, что вы впервые поняли эти основы, прежде чем беспокоиться об этом более продвинутом материале.

  1. Функции создаются только в то время, когда вызывается defun. (Интерпретатор не «смотрит в будущее»).
  2. Одним из атрибутов символа является ссылка на функцию. (Сама функция фактически не имеет имени.)
  3. Несколько символов могут ссылаться на одну и ту же функцию. ((setf (symbol-function 'd) (symbol-function 'b)))
  4. Определение функции a, которая вызывает функцию b (говоря просторечие), в порядке до тех пор, как символ b имеет соответствующую функцию по времени a называется. (Не требуется во время defun ning a.)
  5. Символ может ссылаться на различные функции в разное время. Это влияет на любые функции, «вызывающие» этот символ.

Правила для макросов являются разные (их разложения являются статическими после «читать» время), но многие принципы остаются теми же (Лисп не «смотреть вперед в файл», чтобы найти их) , Поймите, что программы Lisp гораздо более динамичны и «время выполнения», чем большинство (меньших ;-)) языков, к которым вы можете привыкнуть.Поймите , что происходит , когда во время выполнения программы Lisp и правила, регулирующие расширение макросов, начнут иметь смысл.

 Смежные вопросы

  • Нет связанных вопросов^_^