想象一下,我的意图是修改传递给宏的s表达式的语法树,修改语法树,然后评估修改后的参数。我可以做类似以下的事情:
(defmacro runargs [args]
(eval (cons (first args) " buckeroo")))
(runargs (println "hi there"))
现在我觉得感觉惯用,因为我在代码中间打了个大eval
。
现在我稍微修改它并提出以下内容:
(defmacro runargs [args]
`(~@(cons (first args) " buckeroo")))
(runargs (println "hi there"))
这解决了eval
问题。但是,我仍然觉得这不是很惯用的。
我的问题是:在Clojure宏中评估修改过的参数的惯用方法是什么?
答案 0 :(得分:2)
你给出的两个例子完全不同。第一个是在宏展开时间评估修改后的s-expression,这几乎肯定不是你想要的。
user=> (defmacro runargs-eval [args]
(eval (cons (first args) " buckaroo")))
#'user/runargs-eval
user=> (macroexpand '(runargs-eval (println "hi there")))
b u c k e r o o
nil
user=> (defmacro runargs [args]
`(~@(cons (first args) " buckeroo")))
#'user/runargs
user=> (macroexpand '(runargs (println "hi there")))
(println \space \b \u \c \k \e \r \o \o)
如果您只是简单地评估恰好包含对宏的调用的单个s表达式,但是如果您正在编译使用宏的代码(如在lambda),宏扩展发生在编译时:
user=> (defn say-hello-eval [x] (runargs-eval (println x)))
b u c k e r o o
#'user/say-hello-eval
user=> (say-hello-eval "hi there")
nil
user=> (defn say-hello [x] (runargs (println x)))
#'user/say-hello
user=> (say-hello "hi there")
b u c k e r o o
nil
宏只是接受未评估表达式的函数,并返回修改后的未计算表达式。如果你真的想要将表达式作为宏展开的一部分进行评估,这将是一个非常不寻常的情况 - 通常你的宏只会返回修改后的表达式,并让Clojure编译器在适当的时候进行评估。
第二个示例中的语法引用并不是必需的 - 语法引用的一般用例是当您将宏参数插入到模板化表达式中时,该表达式包含应解析为命名空间中的某些内容的符号。宏是定义的。如果你的宏真的是一个s表达式的语法转换,那么你可以像处理任何其他Clojure数据结构一样对表达式进行操作:
(defmacro runargs [args]
(cons (first args) " buckeroo"))