我可以在 emacs lisp 宏中放置条件吗?
Can I put condition in emacs lisp macro?
如何实现这样的目标?
(defmacro mood (x)
(if (equal (symbol-name x) "t")
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad")))
)
我的目标是根据参数创建不同的函数。
这样做有什么问题吗?
编辑 2: 你是对的——对于在 expansion-time 评估的代码 完全依赖 关于(未评估的)宏参数的值,我相信根据这些参数有条件地生成宏的返回形式是安全的。
您只需要注意,任何以 动态 值为条件的行为都需要作为扩展形式的一部分进行处理。
(例如,如果宏参数是一个变量,并且您在您的条件下测试变量 的值 ,那么在扩展时进行该测试是不安全的,因为该值在宏的扩展时间和计算扩展代码的时间之间很容易发生变化。)
所以你问题中的具体示例确实是安全的 as-is,因此在这种情况下我的变体(下面)实际上并不是必需的。但是 expansion-time 评估肯定是您总体上要谨慎的事情。
初始答案如下...
宏在编译时展开。 (或者在最新版本的 Emacs 中,如果没有 byte-compiled 版本的库可用,它们通常会在 加载时间 编译 "eagerly")。
在这些情况下,任何不属于由宏返回 的形式 的代码在每个会话中最多被评估一次,但很可能只评估一次 ever 对于给定的代码扩展(而扩展的代码可能会被多次调用)。
如果您需要扩展代码在 run-time 处有条件地执行操作,条件必须是宏返回的表单的一部分。
编辑: 例如,我想你实际上想写的更像是:
(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")))
定义没有问题。实际上,您的代码几乎可以工作:
(defmacro mood (x)
(if (equal x t)
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad"))))
由于 if
在 back-quotes 之外,我们可以直接检查 x
的值。用不同的参数扩展这个定义表明定义了不同的函数:
> (macroexpand '(mood t))
(defalias (quote happy) (function (lambda nil (message "Happy"))))
> (macroexpand '(mood nil))
(defalias (quote sad) (function (lambda nil (message "Sad"))))
如何实现这样的目标?
(defmacro mood (x)
(if (equal (symbol-name x) "t")
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad")))
)
我的目标是根据参数创建不同的函数。 这样做有什么问题吗?
编辑 2: 你是对的——对于在 expansion-time 评估的代码 完全依赖 关于(未评估的)宏参数的值,我相信根据这些参数有条件地生成宏的返回形式是安全的。
您只需要注意,任何以 动态 值为条件的行为都需要作为扩展形式的一部分进行处理。
(例如,如果宏参数是一个变量,并且您在您的条件下测试变量 的值 ,那么在扩展时进行该测试是不安全的,因为该值在宏的扩展时间和计算扩展代码的时间之间很容易发生变化。)
所以你问题中的具体示例确实是安全的 as-is,因此在这种情况下我的变体(下面)实际上并不是必需的。但是 expansion-time 评估肯定是您总体上要谨慎的事情。
初始答案如下...
宏在编译时展开。 (或者在最新版本的 Emacs 中,如果没有 byte-compiled 版本的库可用,它们通常会在 加载时间 编译 "eagerly")。
在这些情况下,任何不属于由宏返回 的形式 的代码在每个会话中最多被评估一次,但很可能只评估一次 ever 对于给定的代码扩展(而扩展的代码可能会被多次调用)。
如果您需要扩展代码在 run-time 处有条件地执行操作,条件必须是宏返回的表单的一部分。
编辑: 例如,我想你实际上想写的更像是:
(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")))
定义没有问题。实际上,您的代码几乎可以工作:
(defmacro mood (x)
(if (equal x t)
`(defun happy ()
(message "Happy"))
`(defun sad ()
(message "Sad"))))
由于 if
在 back-quotes 之外,我们可以直接检查 x
的值。用不同的参数扩展这个定义表明定义了不同的函数:
> (macroexpand '(mood t))
(defalias (quote happy) (function (lambda nil (message "Happy"))))
> (macroexpand '(mood nil))
(defalias (quote sad) (function (lambda nil (message "Sad"))))