取消引用 Clojure 宏中的列表
unquote a list in Clojure macro
我尝试定义一个宏来从特殊形式的文本中提取信息,[0 => "0\n"]
,实际上是 Clojure 中的列表。
现在假设我只想使用宏 get-input
.
获取其中的 first
部分
(println (get-input [0 => "0\n"])) ; i.e. 0
下面的效果很好。
; this works
(defmacro get-input
[expr]
(let [input (first expr)]
input))
但是当我使用 Syntax-Quote,即反引号时,事情变得混乱了。
只是用 ~
取消引用 expr
就让我想到了这个。
虽然实际上我从来没有使用 expr
的 second
部分,即 =>
,但似乎它仍然被评估落后。
CompilerException java.lang.RuntimeException: Unable to resolve symbol: => in this context
; this sucks
(defmacro get-input
[expr]
`(let [input# (first ~expr)]
input#))
我想知道第一个解决方案和第二个解决方案有什么区别。
您收到此异常是因为您试图取消引用 [0 => "0\n"]
表单,这不是有效的 Clojure 代码。您可以通过更改 first
和取消引用操作的顺序来修复它:
(defmacro get-input
[expr]
`(let [input# ~(first expr)]
input#))
宏在编译时展开,宏体被计算。如果它是语法引用的,则仅计算未引用的表达式,但如果没有引用(如在您的第一个宏定义中),则在编译时计算主体。
如果你用你的第一个定义(没有引号)扩展 (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 状态所述:
Vectors, Sets and Maps yield vectors and (hash) sets and maps whose contents are the evaluated values of the objects they contain.
向量的每个元素按顺序求值,这对 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")
我尝试定义一个宏来从特殊形式的文本中提取信息,[0 => "0\n"]
,实际上是 Clojure 中的列表。
现在假设我只想使用宏 get-input
.
first
部分
(println (get-input [0 => "0\n"])) ; i.e. 0
下面的效果很好。
; this works
(defmacro get-input
[expr]
(let [input (first expr)]
input))
但是当我使用 Syntax-Quote,即反引号时,事情变得混乱了。
只是用 ~
取消引用 expr
就让我想到了这个。
虽然实际上我从来没有使用 expr
的 second
部分,即 =>
,但似乎它仍然被评估落后。
CompilerException java.lang.RuntimeException: Unable to resolve symbol: => in this context
; this sucks
(defmacro get-input
[expr]
`(let [input# (first ~expr)]
input#))
我想知道第一个解决方案和第二个解决方案有什么区别。
您收到此异常是因为您试图取消引用 [0 => "0\n"]
表单,这不是有效的 Clojure 代码。您可以通过更改 first
和取消引用操作的顺序来修复它:
(defmacro get-input
[expr]
`(let [input# ~(first expr)]
input#))
宏在编译时展开,宏体被计算。如果它是语法引用的,则仅计算未引用的表达式,但如果没有引用(如在您的第一个宏定义中),则在编译时计算主体。
如果你用你的第一个定义(没有引号)扩展 (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 状态所述:
Vectors, Sets and Maps yield vectors and (hash) sets and maps whose contents are the evaluated values of the objects they contain.
向量的每个元素按顺序求值,这对 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")