関数をローカルでオーバーライドしますが、元の関数の呼び出しを許可します


7

アドバイス機能により、関数の動作をグローバルに変更できます。アドバイス定義は、元の関数を呼び出すことができます。

(defadvice foo
  (around foo-bar activate compile)
  "Always set `qux' to t when running `foo'."
  (let ((qux t))
    ad-do-it))

clパッケージは提供しflet、ローカルの機能を無効にするマクロを。

(defun foo ()
  "global")
(flet ((foo ()
          "local"))
  (some-code-that-calls-foo))

それは元のfoo関数への参照を許可しません。ローカルのオーバーライドで元の関数を呼び出す必要がある場合はどうなりますか?

(defun foo ()
  "global")
(flet ((foo ()
          (concat (foo) "+local")))
  ;; this will cause an infinite loop when (foo) is called
  (some-code-that-calls-foo))

この単純なアプローチは、適切な理由で機能しません(foo)。ローカル定義への再帰的な呼び出しです。

オーバーライドコードから元の関数を呼び出すことができる、関数をローカルでオーバーライドする面倒な方法は何ですか?

アプリケーション:fooグローバルにリバウンドされるべきではないが、コードがオリジナルを呼び出す必要がある場合に、いくつかの既存のコードをサルパッチングします。これが私が望んでいた最新の例です:

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (<global buffer-file-name> (buffer-base-buffer buffer))))
      ad-do-it)))

buffer-file-nameローカルで再バインドし、元のを呼び出したかったbuffer-file-name。この特定のケースでは、buffer-file-name変数を使用するという回避策があります。しかし、ここで私の質問のポイントは、一般的な手法です。関数(ここbuffer-file-name)をローカルにバインドし、再定義からグローバル定義を呼び出すにはどうすればよいですか?

これは私用で、.emacsEmacs 19.34で作業を続けているため、Emacs 24.4を必要とするソリューションは廃止されました。私はレキシカルバインディングをきれいに処理するソリューションを好みますが、モンキーパッチングは本質的に動的バインディングについてです。


私はよく分からないcl-letfのemacs 24.3と前に提供されていますが、ここでは、関連するQ&Aです:emacs.stackexchange.com/a/16495/221
フランソワ・Févotte

回答:


6

(で取得したsymbol-function)元の関数をローカル変数にfuncall格納し、その変数に格納されている関数オブジェクトを呼び出すために使用します。面倒ですが、ほとんど機能します。

(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (let ((original-buffer-file-name (symbol-function 'buffer-file-name)))
    (flet ((buffer-file-name (&optional buffer)
             (funcall original-buffer-file-name (buffer-base-buffer buffer))))
        ad-do-it)))

これはほとんどの場合機能しますが、本来の機能を果たしますが、まれに壊れることがあります。Emacs Lispにはシンボルの関数スロットをローカルに定義する基本的な方法がないため(を使用した可変スロットのみlet)、fletバインディングを設定し、を介してそれらを復元しますunwind-protect。呼び出しのネストが超過したためにコードがmax-lisp-eval-depth停止した場合、またはこの関数の実行中にバインディングが変更された場合(たとえば、アドバイスをデバッグしているため)、シンボルの関数スロットが復元されない可能性があります。誤って一部の機能を失うことのないように予防策を講じることをお勧めします。

別の方法は、関数のコピーを保存することです。これには、元の機能が失われないという利点があります。

(fset 'original-buffer-file-name (symbol-function 'buffer-file-name))
(defadvice TeX-master-file
  (around TeX-master-file-indirect-buffer activate compile)
  "Support indirect buffers."
  (flet ((buffer-file-name (&optional buffer)
           (original-buffer-file-name (buffer-base-buffer buffer))))
      ad-do-it)))

buffer-file-nameは再バインドされる可能性が低い組み込み関数ですが、グローバル関数の再定義を追跡しないため(たとえば、その関数にアドバイスを追加するため)、この特定のケースでは問題ありません。


あなたは持っていますnadvice同じのためのレシピを?
Kaushal Modi

1
@kaushalmodiいいえ。明確なメリットがある場合にのみ、新しい機能を使用します。私はまだ24.3を日常的に使用しているため、Emacs 19(またはそれ以前)から機能しているアドバイスメカニズムを使用しています。
Gilles「SO-邪悪なことをやめなさい」
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.