【发布时间】:2016-01-24 06:51:09
【问题描述】:
如何实现这样的目标?
(defmacro mood (x)
(if (equal (symbol-name x) "t")
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad")))
)
我的目标是根据参数创建不同的函数。 这样做有什么问题吗?
【问题讨论】:
如何实现这样的目标?
(defmacro mood (x)
(if (equal (symbol-name x) "t")
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad")))
)
我的目标是根据参数创建不同的函数。 这样做有什么问题吗?
【问题讨论】:
编辑 2: 你是对的 - 对于在扩展时评估的代码完全依赖(未评估的)宏的值的情况参数,我相信基于这些参数有条件地生成宏的返回形式是安全的。
您只需要注意,任何以动态值为条件的行为都需要作为扩展表单的一部分进行处理。
(例如,如果宏参数是一个变量,并且您在条件下测试了变量的 值,那么在扩展时进行该测试是不安全的,因为该值是可能会在宏的扩展时间和扩展代码被评估的时间之间变化。)
因此,您问题中的具体示例确实是安全的,因此在这种情况下实际上不需要我的变体(如下)。但是,扩展时间评估肯定是您通常需要谨慎对待的事情。
初步答案如下...
宏在编译时展开。 (或者在最新版本的 Emacs 中,如果没有可用的字节编译版本的库,它们通常会在加载时间“急切地”编译)。
在这些情况下,任何不属于宏返回表单一部分的代码在每个会话中最多会被评估一次,但很可能只永远一次对于给定的代码扩展(而扩展的代码可能会被多次调用)。
如果您需要扩展代码在运行时有条件地执行,则条件必须是宏返回的表单的一部分。
编辑:例如,我想你实际上想写一些类似的东西:
(defmacro mood (x)
`(if (equal (symbol-name ,x) "t")
(defun happy ()
(message "Happy"))
(defun sad ()
(message "Sad"))))
尽管您(几乎)不想通过比较符号的symbol-name 来比较符号。您已经假设宏参数将评估为符号,因此只需将符号直接与 eq 进行比较:
(defmacro mood (x)
`(if (eq ,x t)
(defun happy ()
(message "Happy"))
(defun sad ()
(message "Sad"))))
然后例如,(mood 'foo) 扩展为(由 M-x pp-macroexpand-last-sexp 提供):
(if
(eq 'foo t)
(defun happy nil
(message "Happy"))
(defun sad nil
(message "Sad")))
【讨论】:
(let ((foo (symbol-name x))) marco-body)。 let 需要在运行时扩展。我错过了什么吗?
let 部分,以免分散人们的注意力。 expand at compile time 仍然令人困惑。我的defmarco 中的if 条件取决于宏参数的值。它应该无法在编译时实现。但我确实成功编译并运行了它。感谢您推荐pp-macroexpand-last-sexp。我以前使用macroexpand 来检查宏。
定义它没有问题。实际上,您的代码几乎可以工作:
(defmacro mood (x)
(if (equal x t)
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad"))))
由于if 在反引号之外,我们可以直接检查x 的值。用不同的参数扩展这个定义表明定义了不同的函数:
> (macroexpand '(mood t))
(defalias (quote happy) (function (lambda nil (message "Happy"))))
> (macroexpand '(mood nil))
(defalias (quote sad) (function (lambda nil (message "Sad"))))
【讨论】: