Ответ 1
Вот один лайнер, который делает это. Здесь 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
, автоматически присваивает этому объекту среду reset. Первым аргументом proto()
является родительский элемент прото-объекта.
В приведенном выше примере его настройка была настроена так, чтобы переменная acf
ссылалась на версию acf
, которая была вставлена в прото-объект (который совпадает с оригиналом, за исключением того, что его среда была изменена, чтобы быть прото-объект). Когда запускается новая функция acf
plot.acf
- это свободная переменная (т.е. Не определена в acf
), поэтому она просматривается в родительском acf
и является средой в прото-объекте, где находит новый plot.acf
. acf
может иметь другие свободные переменные, но в тех случаях, когда они не найдены в прото-объекте, он смотрит на родителя прото-объекта, который является исходной средой исходного 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 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
, как в примере proto, а только установили его родительский. Фактически, размещение пересмотренного acf
в e
или прото-объект не является строго необходимым, а сделано только в случае прото, потому что proto имеет побочный эффект сброса среды, и это был тот побочный эффект, который мы наблюдали. С другой стороны, необходимо поставить пересмотренный plot.acf
в e
или прото-объект, чтобы он встречался до исходного.
Возможно, вы захотите прочитать эту статью и, в частности, раздел о Proxies, начиная с 21, так как приведенная здесь методика является примером прокси-объект.