【发布时间】:2013-03-16 10:48:43
【问题描述】:
看来我对宏的理解是不完整的。 如何将参数传递给宏,并从函数中调用它们。 代码下面看似简单的代码行不通。
(defmacro bar [arg]
(println (symbol? arg)))
(defn foo [arg]
(bar arg))
(foo 'baz) => nil
【问题讨论】:
看来我对宏的理解是不完整的。 如何将参数传递给宏,并从函数中调用它们。 代码下面看似简单的代码行不通。
(defmacro bar [arg]
(println (symbol? arg)))
(defn foo [arg]
(bar arg))
(foo 'baz) => nil
【问题讨论】:
在定义 foo 时正在评估宏。如果您在 repl 中定义 foo 函数,您会注意到 Clojure 打印的是 true。
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
你需要引用宏的主体,否则它将被评估,因为它的返回值为 nil,foo 将简单地返回 nil。
(defmacro bar [arg]
`(println (symbol? ~arg)))
【讨论】:
宏应该生成代码。它将源表达式作为参数并创建新的源。
让我们看看:
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
它不会生成任何有用的代码。
【讨论】:
(println (symbol? arg))。宏只是一个函数,它必须返回扩展形式。如果您想获得您所描述的内容,您应该将宏的主体更改为 `(println (symbol? ~arg))。
要使宏“工作”,它需要返回代码,通常作为列表表达式。因此,对于您的意图,您需要提供:
(defmacro bar (arg)
`(println (symbol? ,arg)))
(这使用 quasiquote 作为构造列表的方便表示法。)在您使用 bar 的地方,通过运行 defmacro 代码扩展了使用以生成该列表。然后编译该列表。比如:
(progn (bar 10))
展开成
(progn (println (symbol? 10))
它被编译,然后,你运行代码并打印'nil'。你会注意到(bar a) 会产生一个错误'a is not bound',因为扩展是(println (symbol a)),其中a 被评估,可能没有值。 (bar 'a) 返回T。
使用正确的bar 宏,您的函数foo 在编译后将扩展为:
(defun foo (x)
(println (symbol? x))
正确计算 (foo 'a) 和 (foo 10)。
【讨论】: