【问题标题】:Unquote-splice and wrap in vector in a clojure macro在 clojure 宏中取消引用拼接并在向量中换行
【发布时间】:2011-04-24 10:56:57
【问题描述】:

我最近开始学习 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}

【问题讨论】:

  • 另外,像这样从书中发布一段代码可以吗?
  • 只是一个需要注意的小事,它是 unquote-splicing 不是 unquote-slicing
  • @Nicolas,感谢更新

标签: macros clojure


【解决方案1】:

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

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

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

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

【讨论】:

  • 感谢 Verneri,这很有意义 :)
【解决方案2】:

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

【讨论】:

  • 谢谢,不过,如果我用~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 也会抛出同样的错误。
  • 你应该在引用的表达式上调用macroexpand,否则它只会在macroexpand 到达它之前进行评估。 Macroexpand 是一个函数。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-05
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多