在Clojure宏中取消引用列表

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

现在让我们说我只是想用宏get-input来获得它的first部分。

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

下面的一个工作得很好。

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

但是当我使用Syntax-Quote ,即反引用时 ,事情变得混乱。 只要用~表示expr引导我这样做。 实际上,我从来没有使用过exprsecond部分,即=> ,但似乎仍然在后面进行评估。

CompilerException java.lang.RuntimeException:在此上下文中无法解析符号:=>

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

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


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

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

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

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

随着第二个定义的扩展将是类似的(生成的符号将不同)

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

扩展之后,Clojure编译器将评估向量[0 => "0n"] ,正如clojure文档所述:

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

向量的每个元素都按顺序评估,对于0 (评估为0),但不适用于=> ,这不是已知的符号。

可能你正在寻找的是扩展表达式(在你的情况下是矢量),而不对其内容进行评估。 为此,您需要quote ~的结果,例如

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

它们扩展到 - 注意'[...]

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

并给予

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

你会得到这个异常,因为你试图取消[0 => "0n"]表单,这不是一个有效的Clojure代码。 您可以通过更改first和非引用操作的顺序来修复它:

(defmacro get-input
  [expr]
  `(let [input# ~(first expr)]
     input#))
链接地址: http://www.djcxy.com/p/65767.html

上一篇: unquote a list in Clojure macro

下一篇: Macros in Clojure, evaluation and quoting