2011-11-20 6 views
11

Supongamos que estoy llamando a la función PackageFuncA que existe dentro de un paquete de terceros (es decir, una biblioteca de CRAN). PackageFuncA a su vez llama a PackageFuncB dentro del mismo paquete de terceros. ¿Hay alguna manera de llamar a PackageFuncA de manera que cuando llame a PackageFuncB, de hecho llame a mi propia implementación de PackageFuncB? En otras palabras, ¿puedo interceptar la llamada a PackageFuncB?Redirigir/interceptar llamadas de función dentro de una función de paquete

Creo que la solución implica crear mi propia función PackageFuncB y luego llamar a PackageFuncA dentro del mismo entorno y no al entorno de PackageFuncA, pero no pude lograr que funcione con do.call ni eval.

+0

¿Sería más fácil crear su propia PackageFunA y modificar la llamada a PackageFunB para que llame a su función? – joran

+0

Ver '? AssignInNamespace' – Andrie

+0

joran - Prefiero no mantener mi propia versión de PackageFuncA, especialmente porque es más que unas pocas líneas de código. – SFun28

Respuesta

10

Aquí hay un trazador de líneas que lo hace. Aquí PackageFuncA es stats::acf y PackageFuncB es stats:::plot.acf que queremos reemplazar con my.plot.acf. my.plot.acf imprime "Hello" y luego llama al stats:::plot.acf real.

# 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 objeto proto es un entorno tal que cualquier función insertado en el objeto a través de la función proto tiene su entorno restablecer automáticamente a ese objeto. El primer arg de proto() es el elemento primario del objeto proto.

En el ejemplo anterior, se ha configurado para que la variable acf se refiera a la versión de acf que se insertó en el objeto proto (que es igual que el original, excepto que su entorno se ha modificado para ser el objeto proto) Cuando se ejecuta la nueva función acfplot.acf es una variable libre (es decir, no se define en acf) por lo que se busca en el padre acf y ese es el entorno en el objeto proto donde encuentra el nuevo plot.acf. acf podría tener otras variables libres, pero en esos casos, dado que no se encuentran en el objeto proto, se ve en el elemento primario del objeto proto que es el entorno original del documento original acf. En términos de diagramas tenemos aquí donde <- significa lado izquierdo es el padre del lado derecho:

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

y objeto proto contiene tanto el plot.acf y la acf revisada.

También hemos establecido el entorno del nuevo plot.acf en el objeto proto. Podemos o no haber necesitado hacer esto. En muchos casos, no importará. Si fuera importante no establecer el entorno de la nueva plot.acf entonces se llevaría a cabo de esta manera porque proto nunca se pone el medio ambiente de las funciones insertadas usando [[...]]:

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

En este ejemplo, ambos enfoques de trabajo.

Sería posible hacer todo esto con entornos de civil a costa de tener que utilizar varias líneas de código:

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

En este caso no hemos colocado la acf revisada en e como en el proto ejemplo pero solo establece su padre. De hecho, colocar el acf revisado en e o el objeto proto no es estrictamente necesario, pero solo se hizo en el caso de proto porque proto tiene el efecto secundario de restablecer el entorno y fue ese efecto secundario que buscamos.Por otro lado, es necesario poner el plot.acf revisado en e o el objeto proto para que se encuentre antes del original.

Es posible que desee leer este paper y, en particular, la sección sobre Proxies que comienza en la página 21, ya que la técnica que se muestra aquí es un ejemplo de un objeto proxy.

+0

funciona a la perfección! Tendré que mirar esto un poco antes de descubrir por qué. Un breve repaso de lo que está sucediendo sería de gran ayuda, si tiene tiempo. – SFun28

+0

bien. han agregado alguna elaboración. –

+0

¡esto es fantástico! muchas gracias – SFun28

0

Haga una nueva copia de PackageFuncA, restablezca su entorno y escriba su propia versión de PackageFuncB.

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

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

Es posible que tenga que hacer un poco de edición si PackageFuncA utiliza funciones de un-exportada desde su envase original. Además, si no desea que el nuevo PackageFuncB se use en otro lugar, puede envolverlo dentro de su nuevo PackageFuncA en lugar de colocarlo en el entorno global.

Cuestiones relacionadas