Вы спрашиваете о расширении макроса - но я хотел бы уточнить, как обрабатываются функции в первую очередь.
Обратите внимание, когда на самом деле происходят вызовы и определения. В вашей второй точке вы говорите, что код внутри функции может вызывать функцию, определенную позже. Это не совсем верно.
В таких языках, как 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
, оптимизация и другая магия могут выполнять всевозможные надуманные вещи, отличные от того, что я описал выше, но сначала убедитесь, что вы впервые поняли эти основы, прежде чем беспокоиться об этом более продвинутом материале.
- Функции создаются только в то время, когда вызывается
defun
. (Интерпретатор не «смотрит в будущее»).
- Одним из атрибутов символа является ссылка на функцию. (Сама функция фактически не имеет имени.)
- Несколько символов могут ссылаться на одну и ту же функцию. (
(setf (symbol-function 'd) (symbol-function 'b))
)
- Определение функции
a
, которая вызывает функцию b
(говоря просторечие), в порядке до тех пор, как символ b
имеет соответствующую функцию по времени a
называется. (Не требуется во время defun
ning a
.)
- Символ может ссылаться на различные функции в разное время. Это влияет на любые функции, «вызывающие» этот символ.
Правила для макросов являются разные (их разложения являются статическими после «читать» время), но многие принципы остаются теми же (Лисп не «смотреть вперед в файл», чтобы найти их) , Поймите, что программы Lisp гораздо более динамичны и «время выполнения», чем большинство (меньших ;-)) языков, к которым вы можете привыкнуть.Поймите , что происходит , когда во время выполнения программы Lisp и правила, регулирующие расширение макросов, начнут иметь смысл.
http://clhs.lisp.se/Body/03_abab.htm –
Подробное описание макросов см. По адресу: http://www.paulgraham.com/onlisp.html Вы можете скачать книгу оттуда. –