范围 - 计数器功能正常但不在自定义包中

时间:2018-05-20 10:54:25

标签: r

我有一个计数器功能,我喜欢包围另一个功能("有趣"),以帮助记录我称之为多少次。我通过创建一个新环境来跟踪呼叫" counter.env"如果它还没有存在并将计数存储在那里。

counter <- function(fun) {
if (!exists("counter.env", envir = .GlobalEnv)) {
counter.env <<- new.env(parent = globalenv())
assign("i", 0, envir = counter.env)
}
function(...) {
local(i <- i+1, env = counter.env)
fun(...)
}
}

我也有一个功能&#34; get_calls&#34;这只是一个从环境中获取计数的调用。如果用户在他们正在调用的实际功能之前调用它,我会喜欢运行0,无论他们为什么这样做。

get_calls <- function() {
if (!exists("counter.env", envir = .GlobalEnv)) {
counter.env <<- new.env(parent = .GlobalEnv)
assign("i", 0, envir = counter.env)
}
get("i", envir = counter.env)
}

最后让我们说我的包装功能是一个带有自己参数的函数,&#34; fun(arg1)&#34;。所以我把它包起来。

count.and.call <- counter(fun)

我称之为:

count.and.call(arg1) 

立即&#34; counter.env&#34;在我的全局环境中创建,我可以使用get_calls返回调用。

现在,鼓声当我将这些功能放在一个包中,然后我构建包,然后运行

count.and.call(arg1)

counter.env不是在全局环境中创建的。并显示

error in eval(quote(i <- i + 1), counter.env) : 
object 'counter.env' not found

我最关心的是修复我的计数器,这可能与环境范围有关。

但是我也不确定我是否使用了我的计数器功能的最佳实践,如果是的话,我可以得到一些建议吗?

2 个答案:

答案 0 :(得分:2)

最佳做法是您的软件包不应干涉全局环境。如果要存储状态,请在包的命名空间中为其创建环境。您甚至不必自己指定位置,默认情况下会自动发生。

在源文件中:

counter.env <- new.env()

# this gets run every time your package is loaded
.onLoad <- function(libname, pkgname)
{
    counter.env$i <- 0
}

counter <- function(fun)
{
    # do stuff...
   counter.env$i <- counter.env$i + 1
}

reset_counter <- function()
{
    counter.env$i <- 0
}

# necessary if you want the user to see the counter and you don't export counter.env
get_counter <- function()
{
    counter.env$i
}

答案 1 :(得分:2)

另一种非常R-ish方式的方法是使用闭包。例如:

countingFun <- function(fun) {
  count <- 0
  function(x) {
    count <<- count + 1
    fun(x)
  }
}

count <- function(fun) {
  environment(fun)$count
}

这会将计数保留在函数的环境中,该函数是自动创建的,包含调用countingFun的本地变量。然后就可以了

myMean <- countingFun(mean)
mySd   <- countingFun(sd)
myMean(x)
mySd(x)
myMean(x)
count(myMean)  # 2
count(mySd)    # 1

您可能希望向count添加一些错误检查,以确保不会对未被计算的函数调用。