如何编写Clojure宏以获取var的值?

时间:2018-07-31 01:23:30

标签: clojure

user =>(def v-1“这是v1”)

user =>(def v-2“这是v2”)

user =>(defmacro m [v](符号(str“ v-” v)))

user =>(m 1)

“这是v1”

user =>(m 2)

“这是v2”

user =>(让[i 2](m i))

CompilerException java.lang.RuntimeException:无法解析符号:v-i在这种情况下,编译:(NO_SOURCE_PATH:73:12)

我可以同时写一个宏吗

(m 2)

(让[i 2](m i))

获取“这是v2”?

2 个答案:

答案 0 :(得分:6)

没有宏,这是可能的:

(defn m [v] (var-get (resolve (symbol (str "v-" v)))))

(m 1) ;; => "This is v1"
(let [i 2] (m i)) ;; => "This is v2"

如果需要,您也可以使用宏:

(defmacro m [v] `@(resolve (symbol (str "v-" ~v))))

答案 1 :(得分:1)

一个普通的函数似乎更可能是您想要的。

但是,首先要解决最初的问题,如果您要坚持使用宏,宏是在编译时会被调用的常规函数​​,因此您可以使用其符号名称查找Var并获取其使用deref的值,就像您在应用程序的运行时(而不是宏的运行时)那样:

(defmacro var-value [vsym] @(resolve vsym))

(def foo 1)

(var-value foo)
;= 1
(macroexpand-1 '(var-value foo))
;= 1

请注意,上面的1是此处的实际宏扩展。这与

不同
(defmacro var-value [vsym] `@(resolve ~vsym))

因为后者扩展为对resolve的调用,因此将实现的查询推迟到您应用的运行时。

(macroexpand-1 '(var-value foo))
;= (clojure.core/deref (clojure.core/resolve foo))

因此,无论您在哪里调用该宏,该代码都将内联。

当然,宏也可以扩展为符号-例如。

(defmacro prefixed-var [suffix]
  `(symbol (str "v-" ssuffix)))

会产生v-1(对于(prefixed-var 1))等扩展名。

这里,回到宏的适用性主题,但是,如果您使用宏,则产生扩展所需的所有信息必须在编译时可用,因此通常您不能使用的值。 let / loop扩展中的局部变量或函数参数的根本原因是,它们在编译时没有固定值。 1

因此,最干净的方法可能是将resolve调用包装在defn中并调用结果函数–尽管可以肯定地知道,我们需要知道您要尝试的问题是什么通过引入执行Var查找的宏来解决。


1 除非是静态分配的常量值(如问题文本中给出的示例所示),否则我假设您正在考虑一般使用本地语言的运行时值,而不仅仅是使用初始化表达式为常量文字的那些值。