我最近开始学习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}
答案 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替换。