Снимите кавычки-соедините и оберните в вектор в макросе clojure

#macros #clojure

#макросы #clojure

Вопрос:

Я недавно начал изучать clojure и читаю The Joy of Clojure, чтобы разобраться с этим. У меня есть вопрос относительно сегмента кода в главе «Макросы» (8) на странице 166

 (defmacro domain [name amp; body]
  `{:tag :domain,                      ;`
    :attrs {:name (str '~name)},       ;'
    :content [~@body]})
  

Насколько я понимаю, body это структура, подобная последовательности, со всеми аргументами, кроме первого. Если да, то почему в третьей строке мы снимаем кавычки ( ~@ ) и снова помещаем значения в вектор. Почему бы просто не сделать ~body вместо [~@body] ? В чем разница?

Извините, но мне действительно трудно понять всю суть макросов (из python).

Редактировать: После небольшого эксперимента я обнаружил, что это работает,

 (defmacro domain2 [name amp; body]
  `{:tag :domain,                      ;`
    :attrs {:name (str '~name)},       ;'
    :content '~body})
  

и наряду с результатами, которые я получил из ответа Джоста, я думаю, что я знаю, что здесь происходит. body представляется в виде списка, и поэтому, если я не поставлю ' перед ~body , clojure попытается оценить его.

 user=> (domain "sh" 1 2 3)
{:content [1 2 3], :attrs {:name "sh"}, :tag :domain}

user=> (domain2 "sh" 1 2 3)
{:content (1 2 3), :attrs {:name "sh"}, :tag :domain}
  

Комментарии:

1. Кроме того, можно опубликовать фрагмент кода из книги, подобный этому, здесь, верно?

2. Просто небольшая вещь, на которую следует обратить внимание, это сращивание без кавычек, а не срезание без кавычек

Ответ №1:

Я думаю, что ответ заключается в намерении этого макроса.

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

Это правда, что функция emit выдаст аналогичные результаты как с вашим кодом, так и с кодом из книги, но поскольку функция выполняет синтаксический анализ из clojure.xml создает карту с содержимым в векторе, лучше сделать то же самое здесь, чтобы не нарушать другой код, полагающийся на ту же структуру. Возможно, было бы хорошей идеей также использовать здесь struct, чтобы соответствовать clojure.xml , как сейчас, например (content (domain ...)) , не работает.

Думая о структурах данных в целом, я нахожу хорошей идеей использовать здесь индексированную последовательность, подобную vector, поскольку это позволяет, например, ((:content domain-item) 1) , получить доступ ко второму элементу содержимого. Не говоря уже о подпоследовательностях и т.д.

Комментарии:

1. Спасибо Вернери, это имеет большой смысл 🙂

Ответ №2:

Вы совершенно правы; ~body — это уже последовательность, поэтому, если нет необходимости гарантировать, что :content — это вектор (а не просто что-то, что может быть выделено), выражение [~@body] может быть заменено просто на ~body .

Комментарии:

1. Спасибо, однако, если я заменю [~@body] на ~body и сделаю (domain "sh" 1 2 3) , я получу java.lang.ClassCastException: java.lang.Integer cannot be cast to clojure.lang.IFn (NO_SOURCE_FILE:0) . Вызов macroexpand также выдает ту же ошибку.

2. Вы должны вызвать macroexpand для выражения, заключенного в кавычки, иначе оно просто вычислит, прежде чем macroexpand доберется до него. Макрорасширение — это функция.