2015-03-03 32 views
1

想象一下,我的意图是修改传递给宏的s表达式的语法树,方法是修改语法树,然后评估修改后的参数。我可以做类似如下:在Clojure宏中评估修改参数的习惯用法是什么?

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

(runargs (println "hi there")) 

现在,这并不觉得习惯,因为我已经疲惫不堪的我的代码中间的大eval

现在我稍微修改,并拿出了以下内容:

(defmacro runargs [args] 
    `([email protected](cons (first args) " buckeroo"))) 

(runargs (println "hi there")) 

这已经解决了这个问题eval。但我仍然觉得这不太习语。

我的问题是:在Clojure宏中评估修改参数的习惯用法是什么?

+0

你能提供您所需的输出,无论是AST-明智的:如果宏是真正的s表达式的语法变换,那么你可以在像你的数据将任何其他Clojure的数据结构表达操作和打印的? (因为给定的代码不会打印'buckeroo',而是'b u c k e r o o',我不确定这是否是有意的。) – xsc 2015-03-03 11:39:16

+0

我的歉意。我们假设这是有意的。 – hawkeye 2015-03-03 12:20:26

+0

使用宏的'技巧'是实际上有一个清晰的图片,说明一旦宏展开后代码的外观。从这里,你可以确定你想要做什么的模板。一旦你有了这个,那么这个宏是非常直接的定义。在大多数情况下,你不希望宏对参数进行评估 - 它更多的是在编译时将参数重新排列成新的形式,然后在运行时进行评估。如果参数是(inc x )而不是一个println和一个字符串 - 在编译时什么是x?什么时候应该发生? – 2015-03-04 09:06:52

回答

2

你给出的两个例子做了完全不同的事情。首先是在宏展开时间处评估修改的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] 
     `([email protected](cons (first args) " buckeroo"))) 
#'user/runargs 

user=> (macroexpand '(runargs (println "hi there"))) 
(println \space \b \u \c \k \e \r \o \o) 

没有多大的差别,如果你只是简单地评价一个s表达式,恰好包含调用您的宏,但如果你正在编译代码,使用您的宏(如的λ的主体),宏扩展发生在编译时:

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编译器负责在适当的时候评估它。

第二个示例中的语法引用并不是真的必要 - 语法引用的一般用例是,当您将宏参数插入包含符号的模板化表达式中时,该符号应解析为名称空间中的某些该宏被定义。

(defmacro runargs [args] 
    (cons (first args) " buckeroo")) 
+0

钉住了它,谢谢。 – hawkeye 2015-03-07 10:45:03

相关问题