在Clojure宏中取消引用列表

时间:2015-04-11 13:32:17

标签: clojure macros quote

我尝试定义一个宏来从特殊形式的文本[0 => "0\n"]中提取信息,实际上是Clojure中的列表。

现在让我们说我只想用宏first获取get-input部分内容。

(println (get-input [0 => "0\n"])) ; i.e. 0

下面的一个很好用。

; this works
(defmacro get-input
  [expr]
  (let [input (first expr)]
    input))

但是当我使用语法 - 引用,即反引号时,事情变得令人困惑。 只需将expr~取消引用即可引导我这样做。 实际上,我从不使用second的{​​{1}}部分,即expr,但似乎仍然会在后面进行评估。

  

CompilerException java.lang.RuntimeException:无法解析符号:=>在这种情况下

=>

我想知道第一个解决方案和第二个解决方案之间有什么区别。

2 个答案:

答案 0 :(得分:1)

您之前收到此异常是因为您尝试取消引用[0 => "0\n"]表单,这不是有效的Clojure代码。您可以通过更改first的顺序和取消引用操作来修复它:

(defmacro get-input
  [expr]
  `(let [input# ~(first expr)]
     input#))

答案 1 :(得分:1)

在编译时扩展宏,并评估宏的主体。如果它是语法引用的,则只评估未加引号的表达式,但如果没有引号(如在第一个宏定义中),则在编译时评估主体。

如果您使用第一个定义(无引号)展开(get-input [0 => "0\n"]),您将拥有

> (macroexpand '(get-input [0 => "0\n"]))
0

0是宏扩展的结果,这意味着(get-input [0 => "0\n"])的所有调用都将在编译时由0替换。

使用第二个定义,扩展将类似(生成的符号将不同)

> (macroexpand '(get-input [0 => "0\n"]))
(let* [input__11966__auto__ (first [0 => "0\n"])] 
  input__11966__auto__)

扩展后,Clojure编译器将评估向量[0 => "0\n"],并如clojure doc所述:

  

向量,集合和映射产生向量和(散列)集以及映射,其内容是它们包含的对象的求值

矢量的每个元素都按顺序进行评估,这对于0(评估为数字0)是合适的,但不适用于不是已知符号的=>

您正在寻找的是扩展表达式(在您的情况下是向量),不用评估其内容。为此,您需要quote ~的结果,例如

(defmacro get-input-as-str
  [expr]
  `(let [a# (quote ~expr)]
     (map str a#)))

扩展为 - 注意'[...]

> (macroexpand '(get-input-as-str [0 => "0\n"]))
(let* [a__12040__auto__ '[0 => "0\n"]] (map str a__12040__auto__))

并给出

> (get-input-as-str [0 => "0\n"])
("0" "=>" "0\n")