在 clojure 宏中取消引用拼接并包裹在向量中
我最近开始学习 Clojure,并正在阅读《The Joy of Clojure》来掌握它。我对第 166 页的宏章 (8) 中的代码段有疑问
(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}
I have recently started learning clojure and am reading The Joy of Clojure to get to grips with it. I have a question regarding a code segment in the Macros chapter (8), on page 166
(defmacro domain [name & body]
`{:tag :domain, ;`
:attrs {:name (str '~name)}, ;'
:content [~@body]})
As I understand it, body
is a sequence like structure with all arguments except the first one. If so, in the third line, why are we unquote-splicing (~@
) and putting the values in a vector again. Why not just do ~body
instead of [~@body]
? What is the difference?
I am sorry but I am finding it really hard to grasp the whole macros thingy (coming from python).
Edit: After a bit of experimentation, I found that this works,
(defmacro domain2 [name & body]
`{:tag :domain, ;`
:attrs {:name (str '~name)}, ;'
:content '~body})
and along with the results I got from Joost's answer, I think I know what is happening here. body
is being represented as a list, and so if I don't put a '
in front of ~body
, clojure will try to evaluate it.
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}
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我认为答案就在于这个宏的意图。
快速查看提到的页面,似乎其想法是使用地图为域创建数据结构。选择的结构与 clojure.xml 库使用的结构相同。
确实,emit 函数会在您的代码和书中的代码中产生类似的结果,但是由于 clojure.xml 中的函数解析会生成一个包含向量内容的映射,因此最好在这里执行相同的操作,以便不要破坏依赖相同结构的其他代码。在这里也使用结构体以与 clojure.xml 保持一致可能是个好主意,因为现在
(content (domain ...))
不起作用。考虑到一般的数据结构,我发现在这里使用像向量这样的索引序列是一个好主意,因为它可以说例如
((:content domain-item) 1)
来访问第二个项目的内容。更不用说后续等。I think that the answer lies in the intention of this macro.
Quickly looking at the mentioned page it seems the idea is to create a data-structure for the domain using a map. The structure chosen is identical to the one used by clojure.xml library.
It is true that emit function will produce similar results both with your code and the one from the book, but as the function parse from clojure.xml produces a map with content in a vector, it is better to do the same here, in order not to break other code relying on the same structure. It might be a good idea to also use a struct here to be consistent with clojure.xml, as now for example
(content (domain ...))
doesn't work.Thinking of data structures in general i find it a good idea to use an indexed sequence like vector here as it makes it possible to say for example
((:content domain-item) 1)
to access second item of content. Not to mention subsequences etc.你说得很对; ~body 已经是一个序列,因此除非需要保证 :content 是一个向量(而不仅仅是可以进行 seq'ed 的东西),否则 [~@body] 表达式可以只用 ~body 替换。
You're quite right; ~body is already a sequence, so unless there's a need to guarantee that :content is a vector (instead of just something that can be seq'ed) the [~@body] expression can be replaced by just ~body.