Überschreiben Sie eine Funktion lokal, erlauben Sie jedoch Aufrufe der ursprünglichen Funktion

7

Mit der Hinweisfunktion können Sie das Verhalten einer Funktion global ändern. Eine Hinweisdefinition kann die ursprüngliche Funktion aufrufen.

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

Das clPaket enthält das fletMakro zum lokalen Überschreiben einer Funktion.

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

Das erlaubt keinen Verweis auf die ursprüngliche fooFunktion. Was ist, wenn die lokale Überschreibung die ursprüngliche Funktion aufrufen muss?

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

Dieser einfache Ansatz funktioniert aus gutem Grund nicht: Er (foo)ist ein rekursiver Aufruf der lokalen Definition.

Was ist eine nicht umständliche Methode zum lokalen Überschreiben einer Funktion, mit der die ursprüngliche Funktion aus dem Überschreibungscode aufgerufen werden kann?

Anwendung: Affen-Patching eines vorhandenen Codes, in einem Fall, in dem foonicht global zurückgebunden werden sollte, der Code jedoch das Original aufrufen muss. Hier ist das neueste Beispiel, das ich wollte:

(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)))

Ich wollte buffer-file-namelokal neu binden und das Original aufrufen buffer-file-name. Ok, in diesem speziellen Fall gibt es eine Problemumgehung, bei der die buffer-file-nameVariable verwendet wird. Aber der Punkt meiner Frage hier ist die allgemeine Technik. Wie kann ich eine Funktion (hier buffer-file-name) lokal binden, aber die globale Definition aus meiner Neudefinition aufrufen?

Dies ist für meine .emacs, die ich weiterhin in Emacs 19.34 arbeite, sodass Lösungen, die Emacs 24.4 erfordern, nicht verfügbar sind. Ich bevorzuge zwar Lösungen, die mit lexikalischer Bindung sauber umgehen - aber beim Affen-Patching geht es von Natur aus um dynamische Bindung.

Gilles 'SO - hör auf böse zu sein'
quelle
Ich bin nicht sicher, ob das cl-letfin Emacs 24.3 und früher verfügbar ist, aber hier ist eine verwandte Frage
François Févotte

Antworten:

6

Speichern Sie die ursprüngliche Funktion (erhalten mit symbol-function) in einer lokalen Variablen und funcallrufen Sie das in dieser Variablen gespeicherte Funktionsobjekt auf. Umständlich, aber es funktioniert meistens.

(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)))

Dies funktioniert meistens , indem es das tut, was es tun soll, aber es kann in seltenen Fällen brechen. Da Emacs Lisp keine primitive Möglichkeit hat, die Funktionssteckplätze von Symbolen lokal zu definieren (nur variable Steckplätze mit let), fletwerden die Bindungen festgelegt und wiederhergestellt unwind-protect. Wenn der Code stirbt, weil die Verschachtelung von Anrufen überschritten max-lisp-eval-depthwurde oder wenn die Bindung während der Ausführung dieser Funktion geändert wird (z. B. weil Sie den Hinweis debuggen), kann der Funktionssteckplatz des Symbols möglicherweise nicht wiederhergestellt werden. Möglicherweise möchten Sie Vorsichtsmaßnahmen treffen, um zu verhindern, dass einige Funktionen versehentlich verloren gehen.

Eine andere Methode besteht darin, eine Kopie der Funktion zu speichern. Dies hat den Vorteil, dass die ursprüngliche Funktion niemals verloren geht.

(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)))

Dies wäre in diesem speziellen Fall in Ordnung, da buffer-file-namees sich um eine integrierte Funktion handelt, deren Rückprall unwahrscheinlich ist, die jedoch keine Neudefinitionen der globalen Funktion nachverfolgen würde (z. B. um dieser Funktion Ratschläge hinzuzufügen ).

Gilles 'SO - hör auf böse zu sein'
quelle
Hast du ein nadviceRezept dafür?
Kaushal Modi
1
@kaushalmodi Nein. Ich verwende neuere Funktionen nur, wenn sie einen bestimmten Vorteil haben. Ich benutze immer noch täglich 24.3, also halte ich mich an den Beratungsmechanismus, der seit Emacs 19 (oder früher?) Funktioniert hat.
Gilles 'SO - hör auf böse zu sein'