简体   繁体   English

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

[英]How to write an Clojure macro to get var's value?

user=> (def v-1 "this is v1")
user=> (def v-2 "this is v2")
user=> (defmacro m [v] (symbol (str "v-" v)))
user=> (m 1)
"this is v1"
user=> (m 2)
"this is v2"
user=> (let [i 2] (m i))
CompilerException java.lang.RuntimeException: Unable to resolve symbol: v-i in this context, compiling:(NO_SOURCE_PATH:73:12)

Can I write a macro let both我可以写一个宏让两者都

(m 2)

and

(let [i 2] (mi))

get "this is v2" ?得到“这是 v2”?

This is possible without a macro:这在没有宏的情况下是可能的:

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

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

You can use a macro too if you want:如果需要,您也可以使用宏:

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

A plain function seems much more likely to be what you want.一个普通的函数似乎更有可能是你想要的。

First, though, to address the original question, if you wanted to insist on using a macro, macros are regular functions that happen to be called at compile time, so you can look up a Var using its symbolic name and obtain its value using deref just like you could at (your application's, as opposed to your macro's) runtime:首先,为了解决最初的问题,如果您想坚持使用宏,宏是在编译时碰巧调用的常规函数​​,因此您可以使用其符号名称查找 Var 并使用deref获取其值就像您可以在(您的应用程序的,而不是您的宏的)运行时一样:

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

(def foo 1)

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

Note that the above 1 is the actual macroexpansion here.注意上面的1是这里实际的宏展开。 This is different to这与

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

in that the latter expands to a call to resolve , and so the lookup given that implementation is postponed to your app's runtime.因为后者扩展为对resolve的调用,因此鉴于该实现的查找被推迟到您的应用程序的运行时。

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

So this code will just be inlined wherever you call the macro.所以这段代码只会在你调用宏的任何地方内联。

Of course the macro could also expand to a symbol – eg当然,宏也可以扩展为一个符号——例如

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

will produce expansions like v-1 (for (prefixed-var 1) ) etc.将产生像v-1 (for (prefixed-var 1) ) 等的扩展。

Going back to the subject of the suitability of macros here, however, if you use a macro, all the information that you need to produce your expansion must be available at compile time, and so in general you cannot use the values of let / loop locals or function arguments in your expansion for the fundamental reason that they don't have any fixed value at compile time.回到这里宏的适用性主题,但是,如果您使用宏,则生成扩展所需的所有信息必须在编译时可用,因此通常您不能使用let / loop的值扩展中的局部变量或函数参数,其根本原因是它们在编译时没有任何固定值。 1 1

Thus the cleanest approach would probably be to wrap a resolve call in defn and call the resulting function – although of course to know for sure, we'd need to know what problem you were trying to solve by introducing a macro that performs a Var lookup.因此,最干净的方法可能是在defn包装一个resolve调用并调用结果函数——当然,当然要知道,我们需要通过引入一个执行 Var 查找的宏来知道您试图解决什么问题.


1 Except if statically assigned constant values, as in the example given in the question text; 1除非是静态分配的常量值,如问题文本中给出的示例; I'm assuming you're thinking of using runtime values of locals in general, not just those that whose initialization expressions are constant literals.我假设您正在考虑使用一般本地变量的运行时值,而不仅仅是那些初始化表达式是常量文字的值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM