Clojure宏:从地图创建本地变量 [英] Clojure macro: Create local vars from a map

查看:79
本文介绍了Clojure宏:从地图创建本地变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有这个示例代码,通过迭代映射的键值对来创建var。

I have this sample code where I create vars by iterating over the key value pair of the map.

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

工作正常。
Ouput: true 1 2

It works fine. Ouput: "true" 1 2

但是现在我想通过与var相同的映射,但是,它会引发异常。

But now I want to pass the same map as a var but, it thows an exception.

IllegalArgumentException不知道如何从以下位置创建ISeq:clojure.lang.Symbol

IllegalArgumentException Don't know how to create ISeq from: clojure.lang.Symbol

(def ctx {:a 3 :b 4})

(block ctx (if true (prn "true" a b) (prn "false")))


推荐答案

何时不起作用的原因您传递的符号指向包含地图的var,这是因为宏仅查看符号文字,而不是其所指向的值。当您将其传递给地图文字时,宏会看到地图文字。

The reason it doesn't work, when you pass a symbol that refers to a var that holds a map, is because the macro is only seeing the symbol literal — not the value it refers to. When you pass it a map literal, the macro is seeing the map literal.

如果您希望它也支持符号,则需要 resolve 符号所引用的var并获取其值,例如(var-get(解析绑定))

If you wanted it to also support symbols, you'll need to resolve the var the symbol refers to and get its value e.g. (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 将是一系列表单,即使它只包含一个表单。

You also need to "splice" body into the form using ~@, because body will be a sequence of forms even if it only contains one form.

它还可以帮助您查看对宏进行故障排除时的扩展方式:

It can also help to look at how your macro expands when troubleshooting it:

(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宏:从地图创建本地变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆