Clojure宏:从地图创建局部变量[重复]

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Clojure宏:从地图创建局部变量[重复]相关的知识,希望对你有一定的参考价值。

这个问题在这里已有答案:

我有这个示例代码,我通过迭代地图的键值对来创建变量。

(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")))

它工作正常。输出:“true”1 2


但是现在我想将相同的地图作为var传递但是,它会引发异常。

IllegalArgumentException不知道如何从:clojure.lang.Symbol创建ISeq

(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")))

以上是关于Clojure宏:从地图创建局部变量[重复]的主要内容,如果未能解决你的问题,请参考以下文章

从嵌套地图(和矢量)创建 HTML 表格

Access 2010 Web 数据库 - 从局部变量更新文本框值?

Clojure 宏:引用和语法引用

使用 __LINE__ 为不同的变量名创建宏[重复]

在Clojure中对地图矢量进行排序和排序的更清洁方法?

使用 clojure 宏在 reify 调用中自动创建 getter 和 setter