2013-04-10 8 views
7

supponga v'è una funzione di esempio definita in una libreria (precondizione di questa domanda è tutte le definizioni di questa libreria non possono essere modificati, qualcosa come "sola lettura"):come modificare la definizione di una funzione graziosamente

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

I vuole usare questa libreria, ma la funzione sample non possono eguagliare la mia richiesta, quello che voglio è:

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

qualcuno mi ha detto di usare defadvice, ma ho notato che defadvice possibile inserire solo il codice prima o dopo la invocazioni di sample, come:

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

non possono modificare la definizione di sample stessa. Quindi, come posso raggiungere questo con grazia? Devo riscrivere lo stesso sample, chiamato my-sample o sample2?

+1

Mentre ha dato una chiara descrizione del problema, se si dà la situazione reale e la funzione che si desidera ignorare, altre opzioni potrebbero aprirsi. –

+0

@TreyJackson La situazione attuale è un po 'complessa da descrivere, quello che ho scritto sopra è la descrizione più semplice ma migliore di quella situazione, tuttavia ora ho la risposta, grazie. :-) –

risposta

5

delle sds opere risposta, tranne che si presumibilmente desideri solo da consigliare bar quando sample è in esecuzione, in modo avresti bisogno di consigliare campione anche per attivare e disattivare i consigli per bar. Il mio with-temporary-advice macro facilita questo:

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

Nota che se bar è chiamato anche in altri modi, mentre sample è in esecuzione, il consiglio si applica per le chiamate pure, così si dovrebbe tenere conto che, se si tratta di una possibilità.

In alternativa, è possibile utilizzare flet per ridefinire bar quando richiesto. Questo è soggetto alla stessa avvertenza della prima soluzione, ovviamente.

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

Ecco molto più semplice da leggere, ma per ragioni che non capisco flet è, come di Emacs 24.3, non è più a favore. La sua docstring suggerisce invece di utilizzare cl-flet, ma come cl-flet utilizza il binding lessicale, che in realtà non funzionerà. Come meglio potrei dire, sembrava che flet non stia andando via, tuttavia la raccomandazione attuale sembra essere quella di usare i consigli.

noti inoltre che se, all'interno bar, il comportamento indesiderato dipendeva qualche variabile , allora sarebbe preferibile utilizzare un let vincolante per tale variabile anziché il flet vincolante per la funzione.

Edit:

Questi approcci fanno rendere più difficile per vedere ciò che sta accadendo, ovviamente. A seconda della situazione esatta, potrebbe essere preferibile ridefinire semplicemente la funzione sample per fare ciò che si desidera (o scrivere una funzione my-sample per chiamare al suo posto, come suggerito).

+0

Un suggerimento chiave per me è sufficiente, ma tu mi hai dato più di quello che volevo, davvero grazie per la tua risposta dettagliata. :-) –

+0

C'è dflet, che fornisce flet per le nuove versioni di Emacs: https://github.com/sigma/el-x – tkf

+0

Credo che dovresti usare 'unwind-protect' in quella macro. – Svante

3

Si dovrebbe consigliare la funzione bar invece, utilizzando un around consiglio:

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

Oh, grazie, il mio pensiero è così rigido che non mi rendo conto che potrei consigliare "bar" per raggiungere l'obiettivo. –

4

Altri hanno già fornito buone risposte, ma dal momento che alcuni si lamentano disonore 's flet, ti faccio vedere quello che userei:

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

Look ma! No flet e nessun brutto attivatore/disattivo! E quando si C-h f bar ti dirà chiaramente che c'è di più che soddisfa l'occhio. Anche io ero in realtà uso il nuovo advice-add invece:

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

Il mio istinto è che è brutto avere qualcosa consigliato in modo permanente se è necessario solo raramente, ma una maggiore trasparenza è una vittoria ovvia qui, e quando è scritta in termini di questo tipo di test "inibito", la permanenza sembra molto più sensata. Mi piace molto anche che la nuova libreria di consigli stia usando 'defun', dato che potremo effettivamente usare' find-function' su di esso! Questo sarà un enorme miglioramento. Spero ancora che 'flet' * rimanga in Emacs a lungo termine, ma posso vedere i vantaggi del tuo approccio qui. – phils

+0

Sono contento che ti piaccia la nuova struttura di consulenza. Speriamo che 'flet' sparisca ad un certo punto, anche se per motivi di compatibilità con le versioni precedenti dovresti essere sicuro per diversi anni. Si noti che 'cl-letf' non è in uscita, quindi mentre è leggermente più prolisso di' flet' è anche un'alternativa. – Stefan