Разница между использованием списка и обратной галочкой в ​​макросах - PullRequest
9 голосов
/ 23 февраля 2012

На концептуальном уровне макрос в LISP (и диалектах) берет фрагмент кода (в виде списка) и возвращает другой фрагмент кода (снова в виде списка).

На основе вышеуказанного принципа простой макрос можетбыть:

(defmacro zz [a] (list print a))
;macroexpand says : (#<core$print clojure.core$print@749436> "Hello")

Но в ближайшем будущем это также может быть записано как:

(defmacro zz [a] `(print ~a))
;macroexpand says : (clojure.core/print "Hello")

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

Ответы [ 4 ]

8 голосов
/ 23 февраля 2012

Никто еще не указал на это ... разница между вашими двумя макросами заключается в следующем: ваша вторая форма (с использованием backtick)

(defmacro zz [a] `(print ~a))

эквивалентно:

(defmacro zz [a] (list 'print a))

Что отличается от вашего первого примера:

(defmacro zz [a] (list print a))

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

Надеюсь, это объяснение имеет смысл.

6 голосов
/ 23 февраля 2012

Построение списков в явном виде является «самым простым», в некотором смысле, потому что есть несколько основных концепций, которые вам нужно знать: просто примите список и измените его, пока у вас не появится новый список.Backtick - это удобный ярлык для «шаблонных» фрагментов кода;без него можно написать любой макрос, но для любого крупного макроса он быстро становится очень неприятным.Например, рассмотрим два способа записи let в качестве макроса над fn:

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    `((fn [~@names]
        (do ~@body))
      ~@vals)))

(defmacro let [bindings & body]
  (let [names (take-nth 2 bindings)
        vals (take-nth 2 (rest bindings))]
    (cons (list `fn (vec names) (cons `do body))
          vals)))

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

Во втором случае, просто cons и list повсюду, очень сложно понять, как будет выглядеть расширение.Конечно, это не всегда так: иногда бывает проще написать что-то без обратной черты.

Еще один очень важный момент был сделан Кайлом Бертоном: print - это не то же самое, что 'print!Ваше расширение макроса должно содержать символ print, а не его значение (которое является функцией).Встраивание объектов (таких как функции) в код очень хрупко и работает только случайно.Поэтому убедитесь, что ваши макросы расширены до кода, который вы могли бы написать сами, и позвольте системе оценки выполнить тяжелую работу - вы могли бы ввести символ print, но вы не могли ввести указатель на текущее значениефункция print.

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

Между ними есть разница в стиле. Ваш пример очень прост, но в более сложных макросах разница будет больше.

Например, макрос "исключение", как определено в книге "Радость Clojure":

(defmacro unless [condition & body]
    `(if (not ~condition)
        (do ~@body)))

Из книги:

Синтаксическая цитата позволяет следующей if-форме выступать в качестве шаблона для выражения что любое использование макроса становится возможным при его расширении.

При создании макроса всегда выбирайте наиболее читаемый и идиоматический стиль.

Для сравнения, приведенный выше код можно эквивалентно записать так:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))
1 голос
/ 23 февраля 2012

По моему опыту они эквивалентны.Хотя могут быть некоторые крайние случаи, о которых я не знаю. Пример

@ islon можно эквивалентно записать в виде:

Для сравнения вышеприведенный код можно эквивалентно записать так:

(defmacro unless [condition & body]
  (list 'if (list 'not condition)
            (list* 'do body)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...