偶尔会想要修补程序包中的函数,而无需重新编译整个程序包。
例如,在Emacs ESS中,如果未加载install.packages()
,函数tcltk
可能会卡住。有人可能希望修补install.packages()
以便在安装前需要tcltk
并在程序包设置后卸载它。
temp()
的{{1}}修补版本可能是:
install.packages()
现在我们要替换原来的## Get original args without ending NULL
temp=rev(rev(deparse(args(install.packages)))[-1])
temp=paste(paste(temp, collapse="\n"),
## Add code to load tcltk
"{",
" wasloaded= 'package:tcltk' %in% search()",
" require(tcltk)",
## Add orginal body without braces
paste(rev(rev(deparse(body(install.packages))[-1])[-1]), collapse="\n"),
## Unload tcltk if it was not loaded before by user
" if(!wasloaded) detach('package:tcltk', unload=TRUE)",
"}\n",
sep="\n")
## Eval patched function
temp=eval(parse(text=temp))
# temp
,并可能在install.packages()
中插入代码。
为此,没有任何价值:
Rprofile
也就是说,该函数存储在getAnywhere("install.packages")
# A single object matching 'install.packages' was found
# It was found in the following places
# package:utils
# namespace:utils
# with value
#
# ... install.packages() source follows (quite lengthy)
的包/命名空间内。此环境是密封的,因此在更换之前应解锁utils
:
install.packages()
再次使用## Override original function
unlockBinding("install.packages", as.environment("package:utils"))
assign("install.packages", temp, envir=as.environment("package:utils"))
unlockBinding("install.packages", asNamespace("utils"))
assign("install.packages", temp, envir=asNamespace("utils"))
rm(temp)
,我们得到:
getAnywhere()
看起来补丁功能放在正确的位置。
不幸的是,运行它会给出:
getAnywhere("install.packages")
# A single object matching 'install.packages' was found
# It was found in the following places
# package:utils
# namespace:utils
# with value
#
# ... the *new* install.packages() source follows
Error in install.packages(xxxxx) :
could not find function "getDependencies"
是同一个getDependencies()
包中的函数,但未导出;因此它不能在其命名空间之外访问
尽管输出utils
,但修补后的getAnywhere("install.packages")
仍然错位。
问题是我们需要重新加载install.packages()
库以获得所需的效果,这也需要卸载导入它的其他库。
utils
detach("package:stats", unload=TRUE)
detach("package:graphics", unload=TRUE)
detach("package:grDevices", unload=TRUE)
detach("package:utils", unload=TRUE)
library(utils)
现在有效。
当然,我们也需要重新加载其他库。给定依赖关系,使用
install.packages()
应重新加载所有内容。但重新加载library(stats)
库时出现问题,至少在Windows上是这样的:
graphics
(重新)加载library(graphics)
# Error in FUN(X[[i]], ...) :
# no such symbol C_contour in package path/to/library/graphics/libs/x64/graphics.dll
库的正确方法是什么?
答案 0 :(得分:2)
修补程序包中的函数是一个应该避免的低级操作,因为它可能会破坏执行环境的内部假设并导致不可预测的行为/崩溃。如果tck / ESS存在问题(我没有尝试重复),也许它应该修复或者可能有解决方法。特别是更改锁定绑定是需要避免的。
如果您真的想在说install.packages
的开头/结尾处运行一些代码,可以使用trace
。它将完成问题中提到的一些低级操作,但好的部分是,当R的一些新内部发生变化时,你不必担心修复它。
trace(install.packages,
tracer=quote(cat("Starting install.packages\n")),
exit=quote(cat("Ending install packages.\n"))
)
相应地替换tracer
和exit
- 无论如何都不需要exit
,也许您不需要卸载软件包。不过,trace
是一个非常有用的调试工具。
我不确定这是否能解决您的问题 - 如果它可以与ESS一起使用 - 但一般情况下您也可以将install.packages
包装在您定义的函数中,并在工作区中说:
install.packages <- function(...) {
cat("Entry.\n")
on.exit(cat("Exit.\n"))
utils::install.packages(...)
}
这确实是最干净的选择。