首页 > 解决方案 > 为什么/如何一些包在无名环境中定义它们的功能?

问题描述

在我的代码中,我需要检查函数是从哪个包定义的(在我的情况下是exprs():我需要它,Biobase但结果被 覆盖rlang)。
这个 SO question,我想我可以简单地使用environmentName(environment(functionname))。但是对于exprsBiobase该表达式返回的空字符串:

environmentName(environment(exprs))
# [1] ""

检查结构后,environment(exprs)我注意到它的.Generic成员包含包名称作为属性:

environment(exprs)$.Generic
# [1] "exprs"
# attr(,"package")
# [1] "Biobase"

所以,现在我做了这个辅助函数:

pkgparent <- function(functionObj) {
  functionEnv <- environment(functionObj)
  envName <- environmentName(functionEnv)
  if (envName!="") 
    return(envName) else
      return(attr(functionEnv$.Generic,'package'))
}

它完成这项工作并正确返回函数的包名称(如果已加载),例如:

pkgparent(exprs)
# Error in environment(functionObj) : object 'exprs' not found

library(Biobase)

pkgparent(exprs)
# [1] "Biobase"

library(rlang)
# The following object is masked from ‘package:Biobase’:
#   exprs

pkgparent(exprs)
# [1] "rlang"

但我仍然想了解,对于某些包,它们的功能是在“未命名”环境中定义的,而其他包看起来像<environment: namespace:packagename>.

标签: r

解决方案


您在这里看到的是 S4 方法分派工作原理的一部分。实际上,.Generic是 R 方法分派机制的一部分

顺便说一句,rlang 包是一个红鲱鱼:这个问题出现纯粹是由于 Biobase 使用 S4。

但更一般地说,您的解析策略在其他情况下可能会失败,因为还有其他原因(尽管很少)为什么包可能会在单独的环境中定义函数。这样做的原因通常是在某个变量上定义一个闭包。

例如,通常不可能在命名空间级别修改包内定义的变量,因为命名空间在加载时会被锁定。有多种方法可以解决此问题。如果一个包需要一个有状态的函数,一个简单的方法是在一个环境中定义这个函数。例如,您可以定义一个counter在每次调用时增加其计数的函数,如下所示:

counter = local({
    current = 0L

    function () {
        current <<- current + 1L
        current
    }
})

local定义包装函数的环境。

为了应对这种情况,您应该做的是遍历父环境,直到找到命名空间环境。但是有一个更简单的解决方案,因为 R 已经提供了一个函数来为给定环境查找命名空间环境(通过执行所述迭代):

pkgparent = function (fun) {
    nsenv = topenv(environment(fun))
    environmentName(nsenv)
}

推荐阅读