在 Clojure 宏中评估修改后的参数的惯用方法是什么?
What is the idiomatic way to evaluate a modified argument in a Clojure macro?
假设我的意图是修改传递给宏的 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 宏中评估修改后的参数的惯用方法是什么?
你给出的两个例子做的事情完全不同。第一个是在 宏扩展时间 评估修改后的 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
宏只是接受未计算表达式的函数,return 是修改后的未计算表达式。如果您真的想将表达式作为宏展开的一部分进行求值,那将是一种非常不寻常的情况——通常您的宏只会 return 修改后的表达式,并让 Clojure 编译器在适当的时候处理它的求值。
第二个示例中的语法引号并不是真正必要的 - 语法引号的一般用例是当您将宏参数插入到模板化表达式中时,该表达式包含的符号应解析为名称空间中的某些内容定义了宏。如果您的宏确实是 s 表达式的句法转换,那么您可以像操作任何其他 Clojure 数据结构一样将表达式作为数据进行操作:
(defmacro runargs [args]
(cons (first args) " buckeroo"))
假设我的意图是修改传递给宏的 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 宏中评估修改后的参数的惯用方法是什么?
你给出的两个例子做的事情完全不同。第一个是在 宏扩展时间 评估修改后的 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
宏只是接受未计算表达式的函数,return 是修改后的未计算表达式。如果您真的想将表达式作为宏展开的一部分进行求值,那将是一种非常不寻常的情况——通常您的宏只会 return 修改后的表达式,并让 Clojure 编译器在适当的时候处理它的求值。
第二个示例中的语法引号并不是真正必要的 - 语法引号的一般用例是当您将宏参数插入到模板化表达式中时,该表达式包含的符号应解析为名称空间中的某些内容定义了宏。如果您的宏确实是 s 表达式的句法转换,那么您可以像操作任何其他 Clojure 数据结构一样将表达式作为数据进行操作:
(defmacro runargs [args]
(cons (first args) " buckeroo"))