2013-04-10 17 views
7

Załóżmy, że jest to funkcja próbka zdefiniowane w bibliotece (warunek to pytanie jest wszystkie definicje w tej bibliotece nie mogą być modyfikowane, coś jak „tylko do odczytu”):jak zmodyfikować definicję funkcji za łaskawie

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

I aby korzystać z tej biblioteki, ale funkcja sample nie może się równać mój wniosek, co chcę jest:

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

Ktoś powiedział mi użyć defadvice, ale zauważyłem, że defadvice może tylko wstawianie kodu przed lub po wywołań sample, takie jak:

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

nie może zmienić definicję samego sample. Jak mogę to osiągnąć łaskawie? Czy muszę sam przepisać numer sample, zwany my-sample lub sample2?

+1

Podczas dawało bardzo wyraźny opis problemu, jeśli dać rzeczywistą sytuację i działać chcesz zastąpić inne opcje mogą otworzyć. –

+0

@TreyJackson Właściwa sytuacja jest trochę skomplikowana do opisania, to co napisałem powyżej jest najprostszym, ale najlepszym opisem tej sytuacji, jednak teraz otrzymuję odpowiedź, dziękuję. :-) –

Odpowiedz

5

SDS, z wyjątkiem, że prawdopodobnie tylko chcą być doradzanie bar gdy sample realizuje, więc trzeba by doradzić próbkę oraz w celu aktywowania i dezaktywacji porady dotyczącej bar. Moja with-temporary-advice makro ułatwia to:

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

pamiętać, że jeśli bar nazywana jest także w inny sposób podczas sample jest wykonywany, rada będzie ubiegać się o tych połączeń, tak więc należy wyjaśnić, że jeśli jest taka możliwość.

Alternatywnie możesz użyć opcji flet, aby w razie potrzeby zmienić definicję bar. Podlega to temu samemu zastrzeżeniu co pierwsze rozwiązanie.

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

To znacznie prostsze do odczytania, ale ze względów nie rozumiem flet jest, jak Emacs 24.3, nie jest już za. Jego docstring sugeruje użycie zamiast tego cl-flet, ale ponieważ cl-flet używa leksykalnego wiązania, to tak naprawdę nie zadziała. Jak mogłem powiedzieć, brzmiało to tak, jakby flet właściwie nie zniknęło, jednak obecną rekomendacją wydaje się być użycie porady.

Należy również pamiętać, że jeśli wewnątrz bar, niechciane zachowanie zależy od jakiegoś zmiennej, to byłoby korzystne, aby wykorzystywać let wiążące dla tej zmiennej zamiast flet wiążące funkcji.

Edit:

Podejścia te robią to trudniejsze, aby zobaczyć co się dzieje, oczywiście. W zależności od konkretnej sytuacji może być bardziej pożądane niż po prostu przedefiniować funkcję sample, aby zrobić to, co chcesz (lub napisać funkcję my-sample, aby wywołać w jej miejsce, zgodnie z sugestią).

+0

Kluczowa wskazówka dla mnie wystarczy, ale dałeś mi więcej, niż chciałem, naprawdę bardzo dziękuję za szczegółową odpowiedź. :-) –

+0

Istnieje dflet, który zapewnia flet dla nowszych wersji Emacsa: https://github.com/sigma/el-x – tkf

+0

Uważam, że powinieneś użyć 'unwind-protect' w tym makro. – Svante

3

Należy doradzić funkcję bar zamiast przy użyciu around porady:

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

Och, dzięki, moja myśl jest tak sztywna, że ​​nie zdaję sobie sprawy, że mogę doradzić 'bar'owi do osiągnięcia celu. –

4

Inni już pod warunkiem dobrych odpowiedzi, ale ponieważ niektórzy narzekają niesławie flet „s, pokażę co bym użyć:

(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 i żadnych brzydkich aktywacji/deaktywacji! A kiedy C-h f bar wyraźnie powie ci, że jest coś więcej niż oko. Też bym faktycznie używać nowego advice-add zamiast:

(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

Mój instynkt polegał na tym, że brzydko jest mieć coś doradzonego na stałe, jeśli jest to potrzebne tylko nieczęsto, ale większa przejrzystość jest tutaj oczywistą wygraną, a kiedy napisano w kategoriach tego rodzaju testu "powstrzymywania", stałość wydaje się znacznie bardziej rozsądna. Bardzo podoba mi się również to, że nowa biblioteka porad używa 'defun', ponieważ będziemy w stanie użyć' find-function' na tym komputerze! To będzie ogromna poprawa. Nadal mam nadzieję, że 'flet' * pozostanie w Emacs na dłużej, umysł, ale widzę zalety twojego podejścia tutaj. – phils

+0

Cieszę się, że podoba Ci się nowa placówka doradcza. Mam nadzieję, że 'flet' zniknie w którymś momencie, z powodów kompatybilności wstecznej powinieneś być bezpieczny przez kilka lat. Zauważ, że 'cl-letf' nie jest w drodze, więc chociaż jest nieco bardziej gadatliwy niż' flet', to również alternatywa. – Stefan

Powiązane problemy