我有这个示例代码,我通过迭代映射的键值对来创建变量。
(defmacro block [bindings & body]
`(let [
~@(mapcat (fn [[k v]] [(if (symbol? k) k (symbol (name k))) `~v]) bindings) ]
~body))
(block {:a 1 :b 2} (if true (prn "true" a b) (prn "false")))
效果很好。
输出:“真”1 2
但现在我想传递与汽车相同的地图,但是它抛出了异常。
IllegalArgumentException 不知道如何从以下位置创建 ISeq:clojure.lang.Symbol
(def ctx {:a 3 :b 4})
(block ctx (if true (prn "true" a b) (prn "false")))
当您传递一个引用包含映射的 var 的符号时,它不起作用的原因是因为宏只看到符号文字 - 而不是它引用的值。当您向其传递地图文字时,宏会看到地图文字。
如果您希望它也支持符号,您需要resolve
符号引用的 var 并获取其值,例如(var-get (resolve bindings))
:
(defmacro block [bindings & body]
`(let [~@(mapcat (fn [[k v]] [(if (symbol? k)
k
(symbol (name k))) `~v])
(cond
(map? bindings) bindings
(symbol? bindings) (var-get (resolve bindings))
:else (throw (Exception. "bindings must be map or symbol"))))]
~@body))
(block ctx (if true (prn "true" a b) (prn "false")))
"true" 3 4
(block {:a 3 :b 4} (if true (prn "true" a b) (prn "false")))
"true" 3 4
还需要“拼接”body
进入表格使用~@
, 因为body
即使它只包含一个表单,也将是一系列表单。
它还可以帮助您在排除故障时查看宏的扩展方式:
(macroexpand-1 '(block ctx (if true (prn "true" a b) (prn "false"))))
=> (clojure.core/let [a 3 b 4] (if true (prn "true" a b) (prn "false")))
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)