Макрос похож на программиста-ученика, которому вы можете писать заметки:
Иногда, если я пытаюсь что-то отладить, мне хочется изменить что-то вроде
(* 3 2)
Примерно так:
(let [a (* 3 2)] (println "dbg: (* 3 2) = " a) a)
, который работает таким же образом, за исключением того, что выводит только что вычисленное выражение и его значение, а также возвращает значение как результат всего выражения.Это означает, что я могу оставить свой код без помех при проверке промежуточных значений.
Это может быть очень полезно, но это отнимает много времени и подвержено ошибкам.Вы можете вообразить, что делегируете такие задачи своему ученику!
Вместо того, чтобы нанимать ученика, вы можете запрограммировать компилятор так, чтобы он делал это за вас.
;;debugging parts of expressions
(defmacro dbg[x] `(let [x# ~x] (println "dbg:" '~x "=" x#) x#))
Теперь попробуйте:
(* 4 (dbg (* 3 2)))
Он фактически выполняет текстовое преобразование кода для вас, хотя, будучи компьютером, он выбирает нечитаемые имена для своих переменных вместо "a", который я выбрал бы.
Мы можем спросить егочто он будет делать для данного выражения:
(macroexpand '(dbg (* 3 2)))
И это его ответ, так что вы можете видеть, что он действительно переписывает код для вас:
(let* [x__1698__auto__ (* 3 2)]
(clojure.core/println "dbg:" (quote (* 3 2)) "=" x__1698__auto__)
x__1698__auto__)
Попробуйте написать функцию dbgf, которая делает то же самое, и у вас будут проблемы, потому что (dbgf (* 3 2)) -> (dbgf 6) до вызова dbgf, и поэтому, что бы ни делала dbgf, она не может восстановитьсявыражение, которое нужно распечатать.
Я уверен, что вы можете придумать множество способов обойти это, например, оценку во время выполнения или передачу строки.Попробуйте написать dbg, используя defn вместо defmacro.Это будет хороший способ убедить себя в том, что макросы - это хорошая вещь в языке.Если у вас все получилось, попробуйте использовать его для выражения, которое имеет побочный эффект и значение, например
(dbg (print "hi"))
На самом деле макросы настолько хороши, что мы готовы житьс (brackety ((синтаксис))) LISP, чтобы получить их.(Хотя я должен сказать, что мне это нравится само по себе (но тогда (я) немного странный (в голове)).
C также имеет макросы, которые работают примерно так же,но они всегда идут не так, как надо, и чтобы сделать их правильно, вам нужно поместить в программу столько скобок, чтобы она выглядела как LISP!
На самом деле вам не рекомендуется использовать макросы C, потому что они таксклонны к ошибкам, хотя я видел, как они очень эффективно применяли люди, которые действительно знали, что они делают.
Макросы LISP настолько эффективны, что сам язык построен на их основе, как вы заметите, если выпосмотрите на исходные файлы Clojure, которые сами написаны на Clojure.
Базовый язык очень прост, поэтому его легко реализовать, а затем сложная надстройка создается с помощью макросов.
IНадеюсь, это поможет. Это намного дольше, чем мои обычные ответы, потому что вы задали глубокий вопрос. Удачи.