如何调用宏并将参数传递给它

问题描述:

看来我对宏的理解并不完整。 如何将参数传递给宏,并从函数中调用它们。 代码下面看似简单的代码不起作用。如何调用宏并将参数传递给它

(defmacro bar [arg] 
    (println (symbol? arg))) 

(defn foo [arg] 
    (bar arg)) 

(foo 'baz) => nil 

定义foo时正在对宏进行评估。如果你在repl中定义foo函数,你会注意到Clojure输出的是真实的。

user=> (defmacro bar [arg] 
     (println (symbol? arg))) 
#'user/bar 
user=> (defn foo [arg] 
     (bar arg)) 
true ; <= This is the print from your macro 
    ;  (it prints true, because arg is a symbol) 
#'user/foo 

您需要引用宏的主体,否则将被评估,因为它的返回值为零,foo将简单地返回nil。

(defmacro bar [arg] 
    `(println (symbol? ~arg))) 

宏应该生成代码。它将源表达式作为参数并创建新的源。

  1. 你的宏需要什么源?
  2. 你的宏产生了什么源?

让我们来看看:

user=> (defmacro bar [arg] 
     (println (symbol? arg))) 
#'user/bar 
user=> (bar 1) 
false 
nil 
user=> (bar 'a) 
false 
nil 
user=> (bar a) 
true 
nil 

这是扩展:

user=> (macroexpand-1 '(bar a)) 
true 

它不会产生任何有用的代码。

+0

如果我理解正确,那么foo的定义将被扩展为(println(symbol?arg))应该为(foo'baz)打印为真。我在这里错过了什么? – navgeet 2013-03-16 11:40:59

+1

@navgeet,不,你写的宏不会扩展到'(println(symbol?arg))'。宏只是一个函数,它必须返回扩展形式。如果你想得到你所描述的内容,你应该把宏的主体改成'(println(symbol?〜arg))。 – 2013-03-16 13:20:20

对于需要“工作”的宏,它需要返回代码,通常作为列表表达式。因此,对于你的意图,你需要提供:

(defmacro bar (arg) 
    `(println (symbol? ,arg))) 

(这将使用quasiquote作为构建列表更方便的符号),在您使用bar使用扩展,通过运行defmacro码点,产生该清单。该列表然后被编译。因此,例如:

(progn (bar 10)) 

扩展为

(progn (println (symbol? 10)) 

这是编译,然后,以后,你跑被打印的代码和“零”。你会注意到(bar a)将产生'a is not bound'的错误,因为扩展是(println (symbol a)),因此a被评估,可能没有值。 (bar 'a)返回T

有了这个正确bar宏,你的函数foo,在编译时,将扩大到:

其计算(foo 'a)(foo 10)正常。