如何慷慨地修改函数的定义

时间:2013-04-10 02:56:01

标签: emacs lisp elisp

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

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

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

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

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

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

它无法修改sample本身的定义。那么,我怎样才能慷慨地实现这一目标呢?我是否应该自己重写sample,称为my-samplesample2

3 个答案:

答案 0 :(得分:5)

sds的答案有效,除了您可能只想在bar执行时建议sample,所以您还需要建议样本以激活和停用{的建议{1}}。我的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))) 的原因,从Emacs 24.3开始,不再支持。它的文档字符串建议使用flet,但由于cl-flet使用词法绑定,这实际上不起作用。我能说的最好,听起来cl-flet实际上并没有消失,但目前的建议似乎是使用建议。

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

修改

当然,这些方法确实让人更难看到正在发生的事情。根据具体情况,最好只需重新定义flet函数来执行您想要的操作(或编写sample函数来调用它,就像您建议的那样。)

答案 1 :(得分: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)

答案 2 :(得分:3)

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

(defadvice bar (around my-condition)
  (when condition
    ad-do-it))