【问题标题】:How to write an Clojure macro to get var's value?如何编写 Clojure 宏来获取 var 的值?
【发布时间】:2018-07-31 01:23:30
【问题描述】:
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)

我可以写一个宏让两者兼而有之

(m 2)

(let [i 2] (m i))

得到“这是 v2”?

【问题讨论】:

    标签: clojure


    【解决方案1】:

    没有宏也可以:

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

    【讨论】:

    • 谢谢。更多问题,如果我有: (defmacro m [v] `(symbol (str "v-" ~v))) 那么: (let [i 2] (m i)) 可以得到一个 'v-2 符号,是吗有可能获得价值吗?
    • 再次感谢。所以,这个要求不能直接通过宏返回var的值(没有resolve和var-get等),对吧?
    • @jamesqiu AFAIK,您必须以某种方式按名称解析 var,例如resolveinternclojure.lang.Var/find等,然后获取它的值。
    • 请注意,(defmacro m [v] @(resolve (symbol (str "v-" ~v))))` 只是扩展为 @(resolve (symbol (str "v-" ~v))),即实际查找发生在运行时,代码执行在宏的调用者中内联的查找。这相当于在需要此模式的任何地方手动编写@(resolve …)。更简洁的解决方案将使用具有相同主体的函数减去语法引用 + 取消引用。
    【解决方案2】:

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

    不过,首先,为了解决最初的问题,如果您想坚持使用宏,宏是恰好在编译时调用的常规函数​​,因此您可以使用 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

    因此,最简洁的方法可能是在 defn 中包装 resolve 调用并调用生成的函数 - 尽管当然要确定,但我们需要通过引入来知道您要解决的问题一个执行 Var 查找的宏。


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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-05-29
      • 1970-01-01
      • 2010-11-14
      • 1970-01-01
      • 1970-01-01
      • 2023-03-13
      • 2018-05-23
      • 1970-01-01
      相关资源
      最近更新 更多