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 acf
plot.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.
Sarebbe più semplice creare il proprio pacchetto PackageFunA e modificare la chiamata a PackageFunB in modo che invochi la funzione? – joran
Vedere '? AssignInNamespace' – Andrie
joran - Preferirei non mantenere la mia versione di PackageFuncA, soprattutto dal momento che si tratta di più di poche righe di codice. – SFun28