簡體   English   中英

在R中,如何檢查卸載包中是否存在函數?

[英]In R, how can I check for the existence of a function in an unloaded package?

我可以檢查我的環境中是否存在一個函數:

> exists("is.zoo")
[1] FALSE

加載包后,我可以檢查函數是否存在:

> library(zoo)
> exists("is.zoo")
[1] TRUE

但是如何在加載該包的情況下檢查包中是否存在函數?

> exists("zoo::is.zoo")
[1] FALSE

即使未加載函數,也可以查看函數的來源。

> exists("zoo::is.zoo")
[1] FALSE
> zoo::is.zoo
function (object)
inherits(object, "zoo")
<environment: namespace:zoo>

所以你可以用這樣的函數來利用它

exists_unloaded <- function(fn) {
  tryCatch( {
    fn
    TRUE
  }, error=function(e) FALSE
  )
}

如果調用fn錯誤,它將返回FALSE; 如果fn顯示源,則返回TRUE

> exists("zoo::is.zoo")
[1] FALSE
> exists_unloaded(zoo::is.zoo)
[1] TRUE
> exists_unloaded(zoo::is.zootoo)
[1] FALSE

(要小心,因為寫入的exists_unloaded對於所有字符串都返回TRUE 。如果fn是一個字符串,可能想要出錯。)

編輯:

此外,您可以在不加載包的情況下調用函數。 我不知道你的完整用例,但它可能不需要檢查它的存在。 (當然,如果用戶沒有安裝包,它仍然會失敗。)

> exists("zoo::is.zoo")
[1] FALSE
> zoo::is.zoo(1)
> z <- zoo::as.zoo(1)
> zoo::is.zoo(z)
[1] TRUE

如果您不想使用loadNamespace混亂搜索路徑,則可以與getAnywhere一起使用

請注意,這將找到未導出或導出的函數...

loadNamespace('zoo')
x <- getAnywhere('is.zoo')
x[['where']]=='namespace:zoo'
# TRUE

將它包裝在一個函數中

exist_pkg <- function(f, pkg){
  loadNamespace(pkg)
  x <- getAnywhere(f)
  paste0('namespace:',pkg) %in% x[['where']]
}

如果你真的想要,你可以小心地卸載命名空間

您還可以使用getFromNamespace

is.function(getFromNamespace("is.zoo", "zoo"))
# TRUE

這不是一個很好的答案,它可能有一些缺陷,但它是一個開始。

is_exported <- function(fn, pkg){
  nmsp <- readLines(system.file("NAMESPACE", package = pkg))
  nmsp <- paste0(nmsp, collapse = " ")

  Exports <- stringr::str_extract_all(nmsp,
                                   stringr::regex("(?<=export[(]).+?(?=[)])"))
  Methods <- stringr::str_extract_all(nmsp,
                                   stringr::regex("(?<=S3method[(]).+?(?=[)])"))

  any(grepl(stringr::regex(fn), c(Exports, Methods)))
}

is_exported("is.zoo", "zoo")

您可以使用exists函數查看命名空間內部:

exists2 <- function(x) {

    assertthat::assert_that(assertthat::is.string(x))

    split <- base::strsplit(x, "::")[[1]]

    if (length(split) == 1) {
        base::exists(split[1])
    } else if (length(split) == 2) {
        base::exists(split[2], envir = base::asNamespace(split[1]))
    } else {
        stop(paste0("exists2 cannot handle ", x))
    }
}

好的,這需要更准確的答案。

簡短版:你不能。

要了解原因,請考慮包中的以下代碼:

eval(parse(text = paste0("foo <- func", "tion () 1 + 1")))

這將創建一個函數foo 但是你只能通過運行R代碼來了解它。

您可以檢查NAMESPACE文件以進行export(foo) ,但遺憾的是作者可能編寫了類似exportPattern("f.*") ,因此也不可靠。

長版:你不能避免加載包,但你可以避免附加它。 換句話說,R將解釋包源文件(並加載任何dll),並將包存儲在內存中,但它不會直接在搜索路徑上可用。

ns <- loadNamespace(package)
exists("foo", ns)

然后,您可以使用unloadNamespace(package)卸載命名空間。 但請參閱中的警告?detach :這並不總能保證工作! loadNamespace(package, partial = TRUE) 可能會有所幫助,或者devtools::load_alldevtools::unload做一些更聰明的事情,我不知道。

一些答案建議像try{package::foo}這樣的東西。 問題是這本身加載了命名空間:

> isNamespaceLoaded("broom")
[1] FALSE
> try(broom::tidy)
function(x, ...) UseMethod("tidy")
<environment: namespace:broom>
> isNamespaceLoaded("broom")
[1] TRUE

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM