8

假设在库中定义了一个示例函数(这个问题的前提是这个库中的所有定义都不能被修改,比如“只读”):

(defun sample ()
  (foo)
  (bar)
  (baz))

我想使用这个库,但功能sample无法满足我的要求,我想要的是:

(defun sample ()
  (foo)
  (when condition
    (bar))
  (baz))

有人告诉我使用defadvice,但我注意到defadvice只能在调用之前或之后插入代码sample,例如:

(before-advice ...)
(sample)
(after-advice ...)

它不能修改sample自身的定义。那么,我怎样才能优雅地做到这一点呢?我应该重写一个sample自己,称为my-sampleorsample2吗?

4

3 回答 3

5

sds 的答案有效,除了您可能只想在执行bar时提供建议sample,因此您还需要建议 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))

请注意,如果在执行bar时也以其他方式调用sample,建议也将适用于这些调用,因此如果有可能,您应该考虑到这一点。

或者,您可能更喜欢在需要时使用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使用词法绑定,这实际上不起作用。据我所知,这听起来flet并没有真正消失,但目前的建议似乎是使用建议。

另请注意,如果在内部bar,不需要的行为取决于某个变量,那么最好使用let对该变量的绑定而不是对flet函数的绑定。

编辑:

当然,这些方法确实让我们更难看到正在发生的事情。根据具体情况,最好简单地重新定义sample函数以执行您想要的操作(或者my-sample按照您的建议编写一个函数来调用它)。

于 2013-04-10T04:26:41.160 回答
4

其他人已经提供了很好的答案,但是由于有些人抱怨flet' 的耻辱,我将展示我会使用什么:

(defvar my-inhibit-bar nil)
(defadvice bar (around my-condition activate)
  (unless my-inhibit-bar ad-do-it))
(defadvice sample (around my-condition activate)
  (let ((my-inhibit-bar (not condition)))
    ad-do-it))

看妈!没有flet也没有丑陋的激活/停用!当你看到它时,C-h f bar它会清楚地告诉你,还有更多的东西。另外我实际上会使用新的advice-add

(defvar my-inhibit-bar nil)
(defun my-bar-advice (doit &rest args)
  (unless my-inhibit-bar (apply doit args)))
(advice-add :around 'bar #'my-bar-advice)
(defun my-sample-advice (doit &rest args)
  (let ((my-inhibit-bar (not condition)))
    (apply doit args)))
(advice-add :around 'sample #'my-sample-advice)
于 2013-04-10T13:53:41.363 回答
3

您应该bar使用around建议来建议功能:

(defadvice bar (around my-condition)
  (when condition
    ad-do-it))
于 2013-04-10T04:07:18.357 回答