【问题标题】:Clojure macros: resolving varsClojure 宏:解析变量
【发布时间】:2016-06-27 07:43:35
【问题描述】:

我似乎对 Clojure 中的宏很感兴趣。我可能缺少一些基本的东西。首先,请允许我描述一个我想要的示例。

(defmacro macrotest [v] `(d ...))
(def a '(b c))
(macroexpand-1 '(macrotest a))
; => (d (b c))

换句话说,传递给macrotestvar 已解析,但未进一步评估。

为宏提供 var 的值有效:

(defmacro macrotest [v] `(d ~v))
(macroexpand-1 '(macrotest (b c)))
; => (d (b c))

但提供 var 不会:

 (def a '(b c))
 (macroexpand-1 '(macrotest a))
 ; => (d a)

是否可以在 Clojure 宏中解析 var,但不评估其值?

编辑:eval 似乎可以实现我想要的:

  (defmacro macrotest [v] `(d ~(eval v)))
  (def a '(b c))
  (macroexpand-1 '(macrotest a))
  ; => (user/d (b c))

【问题讨论】:

    标签: clojure macros


    【解决方案1】:

    了解宏在编译时评估很重要,而不是在运行时。

    在编译时var a 还没有值,所以它的值没有传递给宏,只有它的名字。但是,当您显式传递“值”时 - 好吧,它在编译时就存在,您会看到它“有效”。

    【讨论】:

    • 谢谢。我知道宏是在编译时评估的,但我可能不明白这一切的含义。
    • 所以我最后一个问题的答案是“否”?没有办法将运行时值传递给宏?
    • 我认为没有办法,除了eval 之类的。在 lisps 中,宏通常用于构建新的“语法”,因此如果您发现在编译时确实必须具有运行时值,也许您应该重新考虑您要实现的目标;)
    • 如果宏扩展成函数,会不会有可能?
    • 使用eval 似乎适用于我的用例(请参阅编辑后的帖子)。我不确定这是否是个好主意。
    【解决方案2】:

    词汇环境

    在宏中使用eval 是不好的做法。当然,以下工作:

    (macroexpand-1 '(macrotest a))
    ; => (user/d (b c))
    

    ...但是,这并没有按预期工作:

    (macroexpand-1 '(let [a "oh no"]
                      (macrotest a)))
    ; => (user/d (b c))
    

    当然,您希望宏评估 a,因为它是在本地定义的,而不是使用绑定到它的全局变量,但您不能,因为宏不会在正确的词法上下文中评估 v。这是理解宏的关键:它们获取代码并生成代码;您还无法使用该代码操作的任何数据。换句话说,扩展宏的时刻可能与评估其结果代码的时间完全无关:在执行宏时评估的任何内容都可能过早地评估 w.r.t。数据的相关性。

    你想做什么?

    有一个名为macrotest 的表单应该接受一个参数v,然后就像你应用了表单d 一样执行。但是:

    • (macrotest (b c)) 不应该评估(b c),只需原封不动地复制它以产生(d (b c))
    • (macrotest a)应该评估a,这会产生一个带引号的表单,并将该表单放在结果代码中,同时返回(d (b c))

    您在这里遇到了问题,因为在一种或另一种情况下,含义会发生根本性的变化。要么你评估一个论点,在这种情况下你必须引用(b c)然后你写:

    (defmacro macrotest [v] `(d ~v))
    

    ... 这要求d 准备好处理带引号的参数;或者您可以接受未评估的论点。 使用与上面相同的d,您可以显式引用参数:

    (defmacro macrotest [v] `(d '~v))
    

    但是,如果d 本身是一个不评估其参数的宏,则必须避免~v 前面的引号。

    【讨论】:

    • 优秀的答案!确实,我忘记了宏中使用的变量的范围。但是,我对在您的答案的第二部分中引用有疑问。如果我使用(defmacro macrotest [v] (d '~v))` 和(def a '(b c)),那么(macrotest a) 会扩展为(d (quote a)) 而不是(d (b c))
    • @jindrichm 谢谢,关于最后一部分你是对的,我可能不太清楚。这里的意图是,在第二种情况下,macrotest 根本不评估其参数,并且仅适用于未引用的文字数据;然后,引用该数据并将其传递给d。因此,给它a 不会计算表达式(就像大多数宏一样)。
    猜你喜欢
    • 1970-01-01
    • 2019-09-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-06-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多