8

ライブラリで定義されたサンプル関数があると仮定します(この質問の前提条件は、このライブラリのすべての定義を変更できないことです。「読み取り専用」のようなものです):

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

このライブラリを使用したいのですが、関数sampleが私の要求に一致しません。私が欲しいのは:

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

を使用するよう誰かに言われましたが、 の呼び出しの前後にのみコードを挿入できることdefadviceに気付きました。defadvicesample

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

それ自体の定義を変更することはできませんsample。では、どうすればこれを優雅に達成できますか? orsampleと呼ばれる自分自身を書き直す必要がありますか?my-samplesample2

4

3 に答える 3

5

sds の答えは機能しますが、おそらくが実行されている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))

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 の時点で、もはや支持されていません。その docstring はcl-flet代わりに使用することを提案していますが、cl-flet字句バインディングを使用しているため、実際には機能しません。私が知るflet限り、実際にはなくなっていないように聞こえましたが、現在の推奨事項は代わりにアドバイスを使用することです.

また、 の内部で、望ましくない動作が変数barに依存している場合は、関数のバインディングではなく、その変数のバインディングを使用することをお勧めします。letflet

編集:

もちろん、これらのアプローチでは、何が起こっているのかを理解するのが難しくなります。正確な状況によっては、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代わりに new を使用します。

(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 に答える