Как раз тогда, когда я подумал, что у меня довольно хорошая работа с макросами, я наткнулся на источник для some
, который на первый взгляд показался мне немного странным.
(defn some
[pred coll]
(when (seq coll)
(or (pred (first coll)) (recur pred (next coll)))))
Моим первым инстинктом было то, чтоПохоже, что это будет потреблять стек, но потом я вспомнил: «Нет, фиктивный, or
- это макрос, поэтому он просто расширится до тонны вложенных ifs
».
Как бы то ни было, обдумывая это немного больше, я в конечном итоге подумал, что заглянул в угол.Во время расширения источник функции будет выглядеть следующим образом:
(defn some
[pred coll]
(when (seq coll)
(let [or__4469__auto__ (pred (first coll))]
(if or__4469__auto__
or__4469__auto__
(recur pred (next coll))))))
Теперь меня смущает последний заключительный вызов recur
.Я всегда думал, что расширение макросов происходит до времени выполнения, но здесь вы должны фактически вызывать уже расширенный код во время выполнения для второго макроэкспорта ... подождите секунду, я думаю, что я только что понял это.
Нет второго макроразложения, нет вложенных блоков if
, только один блок if
.Вызов recur
просто продолжает повторное связывание pred
и coll
, но один и тот же блок, приведенный выше, продолжает проверять правду до тех пор, пока не найдет его или коллекция не закончится и не будет возвращено nil
.
Можеткто-то подтвердит, если это правильная интерпретация?Сначала я смущал себя, думая, что будет чередование макроразложения и времени выполнения, когда во время выполнения вызов recur каким-то образом приведет к новому вызову макроса, что не имеет смысла, так как макро-расширение должно произойти до выполнения.Теперь я думаю, что вижу, где была моя путаница, есть только одно расширение макроса, и полученный код снова и снова используется в цикле.