我尝试定义一个宏来从特殊形式的文本[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:无法解析符号:=>在这种情况下
=>
我想知道第一个解决方案和第二个解决方案之间有什么区别。
答案 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")