Вот одна строка, которая делает это. Здесь PackageFuncA
равно stats::acf
, а PackageFuncB
равно stats:::plot.acf
, которое мы хотим заменить на my.plot.acf
. my.plot.acf
печатает "Hello"
и затем вызывает реальный 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)
Прото-объект - это среда, в которой любая функция, вставленная в объект через функцию proto
, автоматически восстанавливает свой объект в этом объекте. Первый аргумент proto()
является родителем прототипа.
В приведенном выше примере она была настроена таким образом, что переменная acf
ссылается на версию acf
, которая была вставлена в объект прото (то же самое, что и оригинал, за исключением того, что его среда была изменена для объект прото). Когда запускается новая функция acf
, plot.acf
является свободной переменной (т. Е. Не определена в acf
), поэтому она ищется в родительском объекте acf
, и это среда в объекте прото, где она находит новый plot.acf
. acf
может иметь другие свободные переменные, но в тех случаях, когда они не найдены в объекте Proto, он смотрит на родительский объект объекта Pro, который является исходной средой исходного acf
. В терминах диаграмм мы имеем это где <-
означает, что левая сторона является родителем правой стороны:
environment(stats::acf) <- proto object <- revised acf
и объект прото содержит как plot.acf
, так и исправленный acf
.
Мы также установили среду нового plot.acf
для объекта прото. Мы могли или не могли сделать это. Во многих случаях это не имеет значения. Если бы было важно не устанавливать среду нового plot.acf
, то это было бы сделано так, потому что proto никогда не устанавливает среду функций, вставленных с использованием [[...]]
:
acf <- with(p <- proto(environment(acf), acf = stats::acf), acf)
p[["plot.acf"]] <- my.plot.acf
В этом примере оба подхода работают.
Было бы возможно сделать все это в простых средах за счет необходимости использовать несколько строк кода:
# 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)
В этом случае мы не поместили исправленный acf
в e
, как в прототипе, а только установили его родительский элемент. Фактически, размещение измененного acf
в e
или объекте proto не является строго необходимым, но это было сделано только в случае с proto, поскольку у proto есть побочный эффект сброса окружения, и это был тот побочный эффект, который был после нас. С другой стороны, необходимо поместить исправленный plot.acf
в e
или объект прото, чтобы он встречался до исходного.
Возможно, вы захотите прочитать эту статью и, в частности, раздел, посвященный прокси, начиная со страницы 21, поскольку показанная здесь методика является примером прокси-объекта.