2016-10-26 66 views
0

偶爾需要修補程序包中的函數,而無需重新編譯整個程序包。R:修補程序包功能並重新加載基礎庫

例如,在Emacs ESS中,如果未加載tcltk,函數install.packages()可能會卡住。有人可能想要在安裝之前補丁install.packages()以便要求tcltk,並且在安裝軟件包之後卸載它。

install.packages()一個temp()修補版本可能是:

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

應重新載入所有內容。

library(graphics) 
# Error in FUN(X[[i]], ...) : 
# no such symbol C_contour in package path/to/library/graphics/libs/x64/graphics.dll 

這是(重新)的正確方法加載graphics庫:但重裝時graphics庫,至少在Windows上是一個問題嗎?

+0

在描述示例的目的時,你失去了我,但當我看到eval(parse())時,我停止閱讀。 – Roland

回答

2

軟件包中的修補功能是一個應該避免的低級操作,因爲它可能會破壞執行環境的內部假設並導致不可預知的行爲/崩潰。如果tck/ESS出現問題(我沒有試圖重複這個問題),可能應該修復或者可能有解決方法。特別改變鎖定綁定是可以避免的。

如果你真的想在install.packages的開始/結尾運行一些代碼,你可以使用trace。它會做一些在這個問題中提到的低級操作,但最好的部分是你不必擔心在R的一些新內部發生變化時解決這個問題。

trace(install.packages, 
    tracer=quote(cat("Starting install.packages\n")), 
    exit=quote(cat("Ending install packages.\n")) 
) 

更換tracerexit相應的 - 也許exit不需要無論如何,也許你並不需要卸載該程序包。仍然,trace是一個非常有用的調試工具。

我不知道是否會解決您的問題 - 它是否會與ESS工作 - 但一般而言,您還可以在你定義一個函數包裝install.packages說,在您的工作空間:

install.packages <- function(...) { 
    cat("Entry.\n") 
    on.exit(cat("Exit.\n")) 
    utils::install.packages(...) 
} 

這是確實是最乾淨的選擇。

+0

+1我知道跟蹤,在這個術語中沒有想到。 'install.package < - ...'的問題在於它混淆了'ls()'的輸出 – antonio