在Clojure宏中计算修改参数的惯用方法是什么?

在Clojure宏中计算修改参数的惯用方法是什么?,clojure,macros,arguments,eval,Clojure,Macros,Arguments,Eval,想象一下,我的意图是修改传递到宏中的s表达式的语法树,方法是修改语法树,然后计算修改后的参数。我可以这样做: (defmacro runargs [args] (eval (cons (first args) " buckeroo"))) (runargs (println "hi there")) (defmacro runargs [args] `(~@(cons (first args) " buckeroo"))) (runargs (println "hi there")

想象一下,我的意图是修改传递到宏中的s表达式的语法树,方法是修改语法树,然后计算修改后的参数。我可以这样做:

(defmacro runargs [args]
  (eval (cons (first args) " buckeroo")))

(runargs (println "hi there"))
(defmacro runargs [args]
  `(~@(cons (first args) " buckeroo")))

(runargs (println "hi there"))

现在我觉得不习惯,因为我的代码中有一个大的<代码> EVA/COD>。p> 现在我稍微修改一下,得出以下结论:

(defmacro runargs [args]
  (eval (cons (first args) " buckeroo")))

(runargs (println "hi there"))
(defmacro runargs [args]
  `(~@(cons (first args) " buckeroo")))

(runargs (println "hi there"))
这解决了
eval
问题。但我还是觉得这不太习惯


我的问题是:在Clojure宏中计算修改参数的惯用方法是什么?

您给出的两个示例做的事情完全不同。第一个是在宏扩展时评估修改后的s表达式,这几乎肯定不是您想要的

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

你能提供你想要的输出,包括AST和印刷品吗?(因为给定的代码不会打印
buckeroo
,而是
b u c k e r o
,我不确定这是否是有意的。)我道歉。让我们假设这是有意的。宏的“诀窍”是,一旦宏被展开,就可以清楚地了解您希望代码的样子。从中,您可以确定您正在尝试执行的操作的模板。一旦有了它,宏就可以相当直接地定义了。在大多数情况下,您不希望宏来评估参数——它更多的是在编译时将参数重新排列成新的形式,然后在运行时进行评估。如果参数是(Inc x)而不是PrtLn和字符串——编译时X是什么,请考虑宏将做什么?何时应该出现Inc?