假设在一个库中定义了一个示例函数(这道题的前提是这个库中的所有定义都不能被修改,比如“只读”):
(defun sample ()
(foo)
(bar)
(baz))
我想使用这个库,但是函数sample
不能满足我的要求,我想要的是:
(defun sample ()
(foo)
(when condition
(bar))
(baz))
有人告诉我使用defadvice
,但我注意到defadvice
只能在调用sample
之前或之后插入代码,例如:
(before-advice ...)
(sample)
(after-advice ...)
它不能修改sample
本身的定义。那么,我怎样才能优雅地实现这一目标呢?我是否必须自己重写一个名为 my-sample
或 sample2
的 sample
?
最佳答案
sds 的答案有效,除了你可能只想在 sample
执行时通知 bar
,所以你需要通知 sample 以及为了激活并停用 bar
的建议。我的 with-temporary-advice
宏有助于实现这一点:
(defmacro with-temporary-advice (function class name &rest body)
"Enable the specified advice, evaluate BODY, then disable the advice."
`(unwind-protect
(progn
(ad-enable-advice ,function ,class ,name)
(ad-activate ,function)
,@body)
(ad-disable-advice ,function ,class ,name)
(ad-activate ,function)))
(defadvice bar (around my-conditional-bar disable)
;; This advice disabled by default, and enabled dynamically.
(when condition
ad-do-it))
(defadvice sample (around my-sample-advice activate)
"Make execution of `bar' conditional when running `sample'."
(with-temporary-advice 'bar 'around 'my-conditional-bar
ad-do-it))
请注意,如果在执行 sample
时还以其他方式调用了 bar
,则该建议也将适用于这些调用,因此您应该考虑到这一点一种可能性。
或者,您可能更喜欢在需要时使用 flet
来重新定义 bar
。当然,这与第一个解决方案一样需要注意。
(defadvice sample (around my-sample-advice activate)
"Make execution of `bar' conditional when running `sample'."
(if condition
ad-do-it
(flet ((bar () nil))
ad-do-it)))
这多 更易于阅读,但出于我不理解的原因,flet
从 Emacs 24.3 开始不再受青睐。它的文档字符串建议改用 cl-flet
,但由于 cl-flet
使用词法绑定(bind),所以这实际上行不通。据我所知,听起来 flet
实际上并没有消失,但目前的建议似乎是改用建议。
另请注意,如果在 bar
中,不需要的行为取决于某些变量,那么最好使用 let
绑定(bind)在该变量上,而不是在函数上绑定(bind) flet
。
编辑:
当然,这些方法确实使查看正在发生的事情变得更加困难。根据具体情况,最好重新定义 sample
函数来执行您想要的操作(或者编写一个 my-sample
函数来调用它的位置,正如您所建议的那样)。
关于emacs - 如何优雅地修改函数的定义,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15916124/