2011-11-20 9 views
11

Supponiamo che stia chiamando la funzione PackageFuncA che esiste all'interno di un pacchetto di terze parti (ad esempio una libreria da CRAN). PackageFuncA a sua volta chiama PackageFuncB all'interno dello stesso pacchetto di terze parti. C'è un modo per chiamare PackageFuncA in modo tale che quando chiama PackageFuncB, in realtà chiamerà la mia implicazione di PackageFuncB? In altre parole, posso intercettare la chiamata a PackageFuncB?Chiamate di funzione di reindirizzamento/intercettazione all'interno di una funzione di pacchetto

Penso che la soluzione implichi la creazione della mia funzione PackageFuncB e quindi chiamare PackageFuncA nello stesso ambiente e non nell'ambiente di PackageFuncA, ma non riuscivo a farlo funzionare con do.call né eval.

+0

Sarebbe più semplice creare il proprio pacchetto PackageFunA e modificare la chiamata a PackageFunB in modo che invochi la funzione? – joran

+0

Vedere '? AssignInNamespace' – Andrie

+0

joran - Preferirei non mantenere la mia versione di PackageFuncA, soprattutto dal momento che si tratta di più di poche righe di codice. – SFun28

risposta

10

Ecco un one-liner che lo fa. Qui PackageFuncA è stats::acf e PackageFuncB è stats:::plot.acf che vogliamo sostituire con my.plot.acf. my.plot.acf stampa "Hello" e quindi chiama il numero reale stats:::plot.acf.

# we want this to run in place of stats:::plot.acf 
my.plot.acf <- function(x, ...) { cat("Hello\n"); stats:::plot.acf(x, ...) } 

# this does it 
library(proto) 
acf <- with(proto(environment(acf), acf = stats::acf, plot.acf = my.plot.acf), acf) 

# test 
acf(1:10) 

Un oggetto proto è un ambiente tale che qualsiasi funzione inserita nell'oggetto tramite la funzione proto ha il suo ambiente ripristinare automaticamente a tale oggetto. Il primo argomento di proto() è il genitore dell'oggetto proto.

Nell'esempio precedente, il suo stato regolato in modo che la variabile acf si riferisce alla versione di acf che è stato inserito nell'oggetto proto (che è la stessa come l'originale, tranne il suo ambiente è stato modificato per essere oggetto proto). Quando viene eseguita la nuova funzione acfplot.acf è una variabile libera (ad esempio, non definita in acf), quindi viene cercata nel genitore acf e questo è l'ambiente nell'oggetto proto dove trova il nuovo plot.acf. acf potrebbe avere altre variabili libere ma, in quei casi, poiché non sono presenti nell'oggetto proto, esso appare nel genitore dell'oggetto proto, che è l'ambiente originale dell'originale acf. In termini di diagrammi abbiamo questa dove <- significa lato sinistro è madre di destra:

environment(stats::acf) <- proto object <- revised acf 

e l'oggetto proto contiene sia il plot.acf e rivisto acf.

Abbiamo anche impostato l'ambiente del nuovo plot.acf nell'oggetto proto. Potremmo o non potremmo aver avuto bisogno di farlo. In molti casi non importa. Se fosse importante non impostare l'ambiente del nuovo plot.acf allora sarebbe stato fatto in questo modo perché proto mai imposta l'ambiente di funzioni inseriti utilizzando [[...]]:

acf <- with(p <- proto(environment(acf), acf = stats::acf), acf) 
p[["plot.acf"]] <- my.plot.acf 

In questo esempio, entrambi gli approcci di lavoro.

Sarebbe possibile fare tutto questo con gli ambienti di pianura a scapito di dover utilizzare diverse righe di codice:

# create new environment whose parent is the original acf's parent 
e <- new.env(parent = environment(stats::acf)) 

# the next statement is only need to overwrite any acf you already might have from 
# trying other code. If you were sure there was no revised acf already defined 
# then the next line could be omitted. Its a bit safer to include it. 
acf <- stats::acf 

# This sets the environment of the new acf. If there were no acf already here 
# then it would copy it from stats::acf . 
environment(acf) <- e 

# may or may not need next statement. In this case it doesn't matter. 
environment(my.plot.acf) <- e 

e$plot.acf <- my.plot.acf 

acf(1:10) 

In questo caso non abbiamo posto la rivista acf in e come nel proto esempio, ma imposta solo il suo genitore. In effetti, collocare il acf modificato in e o l'oggetto proto non è strettamente necessario, ma è stato fatto solo nel caso di proto perché proto ha l'effetto collaterale di reimpostare l'ambiente ed era quell'effetto collaterale che cercavamo.D'altra parte è necessario inserire il plot.acf modificato in e o l'oggetto proto in modo che venga rilevato prima di quello originale.

Si potrebbe leggere questo paper e, in particolare, la sezione su Proxy a partire da pagina 21 poiché la tecnica mostrata qui è un esempio di oggetto proxy.

+0

funziona perfettamente! Dovrò fissarlo un po 'prima di capire perché. Un breve passaggio di ciò che sta accadendo sarebbe di grande aiuto, se ne hai il tempo. – SFun28

+0

ok. ho aggiunto qualche elaborazione. –

+0

questo è fantastico! grazie mille – SFun28

0

Creare una nuova copia di PackageFuncA, reimpostare l'ambiente e scrivere la propria versione di PackageFuncB.

environment(PackageFuncA) <- globalenv() # makes a new copy of PackageFuncA 

PackageFuncB <- function(...) .... # will be called from your new PackageFuncA 

Potrebbe essere necessario fare un po 'di editing se PackageFuncA utilizza funzioni non esportate dalla confezione originale. Inoltre, se non si desidera utilizzare il nuovo PackageFuncB altrove, è possibile avvolgerlo all'interno del nuovo PackageFuncA anziché collocarlo nell'ambiente globale.

Problemi correlati