Unquote-splice和包裹在clojure宏的向量中

时间:2011-04-24 10:56:58

标签: macros clojure

我最近开始学习clojure并正在阅读 Clojure的喜悦来掌握它。我对章节(8)中的代码段有疑问,第166页

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

据我了解,body是一个类似于序列的结构,除了第一个参数之外的所有参数。如果是这样,在第三行中,为什么我们要取消引用拼接(~@)并将值再次放入向量中。为什么不~body而不是[~@body]?有什么区别?

我很抱歉,但我发现很难掌握整个宏(来自python)。

编辑经过一些实验,我发现这很有效,

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

以及我从Joost回答中得到的结果,我想我知道这里发生了什么。 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}

2 个答案:

答案 0 :(得分:2)

我认为答案在于这个宏的意图。

快速查看提到的页面,似乎想法是使用地图为域创建数据结构。选择的结构与clojure.xml库使用的结构相同。

确实,emit函数会产生与你的代码和书中的代码相似的结果,但是当clojure.xml中的函数解析生成一个包含向量内容的地图时,最好在这里做同样的事情。 ,为了不破坏依赖于相同结构的其他代码。在这里使用struct来与clojure.xml保持一致可能是一个好主意,因为现在例如(content (domain ...))不起作用。

一般来说,对数据结构的思考我发现在这里使用像vector这样的索引序列是一个好主意,因为它可以说例如((:content domain-item) 1)来访问第二项内容。更不用说子序列等了。

答案 1 :(得分:1)

你说得对; 〜body已经是一个序列,所以除非需要保证:content是一个向量(而不仅仅是可以seq'ed的东西),[〜@ body]表达式可以被~body替换。