【问题标题】:R "no visible binding for global variable" note when creating variables in sub-routines and returning to environmentR“全局变量没有可见绑定”在子例程中创建变量并返回环境时注意
【发布时间】:2019-12-18 06:26:57
【问题描述】:

我正在尝试向 CRAN 提交一个包。我的函数很长,几千行。我重写了它并将其分解为一个包装器(“外部”)函数,该函数调用一组“内部”子函数(未导出),这些子函数创建了我想要返回到包装器函数环境的对象。我尝试过使用 assign() 函数或 list2env(),它们的作用相同,只是它接受一个列表作为参数并返回在列表中命名为它们的命名元素的对象。当我对我的包运行 R CMD 检查时,会触发“全局变量没有可见绑定”警告,因为在子函数中创建了许多变量并从这些函数中返回到环境中,并在包装​​器环境中使用之后没有在此环境中创建它们的显式实例。

我之前在网上看到过有关此的问题。其中一些专门处理 ggplot、dplyr 或子集或 data.frame 问题。这个比较笼统。一些在线参考资料提到使用 utils::globalVariables 函数 (https://github.com/r-lib/devtools/issues/1714) 首先将这些变量声明为稍后创建的全局变量。论坛提到要么将它们放在单独的 globals.R 脚本中,要么放在我的包装函数开头的函数调用中。但这种解决方案似乎作为“黑客”而备受争议。另一个解决方案(同样“hackish”,但我想还可以)只是在代码开头将所有这些变量初始化为 NULL。

我看到的另一个解决方案是基本上将所有这些对象存储为在包装函数中初始化的列表的成员,然后返回子函数的所有输出以附加或修改列表项。这样,我要创建的全局对象不是单独的单独对象,而是列表的一部分,所以没有问题。但是,然后我需要特别重写我的代码以将每个对象称为列表项(例如,tmp$obj 而不仅仅是 obj)。另一方面,这在某种程度上更简单,因为所有对象都存储在一个列表中,可以作为一个单元引用和传递,而不必单独跟踪它们。

我想听听有经验的人对这些方法的各种优点/缺点或正确性的看法。

将对象返回到环境

outside_function <- function() {
    k <- letters[17:23]
    #inside_function creates objects m and z which did not exist before               
    inside_function()
    ls()
    print(m)
    print(z)
    inside_function()
    ls()
    #z and m should now be overwritten
    print(m)
    print(z)
}

inside_function <- function() {
    m <- matrix(runif(4), ncol=2)
    z <- letters[1:10]

    #assign to the wrapping environment 
    assign("m", m, envir=parent.frame())
    assign("z", z, envir=parent.frame())
    #an equivalent way:
    list2env(list(m=m, z=z), envir=parent.frame())  

}

另一种方式,将对象保存为列表

outside_function <- function() {
    k <- letters[17:23]
    #inside_function creates objects m and z which did not exist before               
    tmp <- inside_function()

    #refer to m and z only as items in tmp
    print(tmp$m)
    print(tmp$z)

    tmp <- inside_function()
    ls()
    #z and m should now be overwritten
    print(tmp$m)
    print(tmp$z)
}

inside_function <- function() {
    m <- matrix(runif(4), ncol=2)
    z <- letters[1:10]

    #return as list items
    list(m=m, z=z)
}

对于第一个,我得到以下注释:

outside_function: no visible binding for global variable 'm'
outside_function: no visible binding for global variable 'z'

【问题讨论】:

  • 这是主观的,但我肯定会支持列表方法。对我来说,拥有自包含并返回特定值的函数似乎要干净得多,而不是让它们与“全局状态”混为一谈。 (“全局状态”在这里并不严格,但在大多数语言中都是这样描述的)。
  • 查看我上面的编辑。我想出了如何使用环境
  • 很高兴听到你想通了。您可以在 Stack Overflow 上回答您自己的问题(我忘记了确切的规则,但我认为您也可以在短时间内“接受”它作为最佳答案),因此请随时将其发布为答案。跨度>

标签: r package environment-variables devtools cran


【解决方案1】:

我构建的包的唯一目的是为环境分配变量时遇到了这个问题。我感觉到你的痛苦。

我的解决方案是将变量初始化为 NULL。此外,我不会真正称其为 hackish,因为大量编程语言(我能想到的最简单的语言是 Visual Basic)要求您在使用变量之前对其进行初始化。上市不是一个坏主意,但正如你所说,它需要大量的重构,可能不值得你花时间。

【讨论】:

  • 真聪明,我可能会继续使用 hacky 方法,尽管这肯定更好!
【解决方案2】:

使用环境的解决方案

所以我想出了如何做到这一点。是的,您可以使用列表方法,但它有些人为。这是正确的方法:在包装函数 outside_function 内定义一个命名的空环境,您想要存储(并在最后返回)的所有对象都写入其中。然后将该环境作为单个参数(如列表)传递给内部函数。在 inside_function 中,您可以实时编辑存储的环境对象,而无需将列表中的对象显式返回给列表对象。它更干净。

outside_function <- function() {
  
  myenv <- new.env(parent = emptyenv())
  #object k exists in local environment, but not myenv
  k <- LETTERS[17:23]
  #assign list of objects to 
  print(ls()) #two objects, k and myenv
  print(ls(myenv))

  print("first run")
  inside_function(env=myenv) 
  print("LS")
  print(as.list(myenv))
  print("second run")
  inside_function(env=myenv)
  print("LS")
  print(as.list(myenv))

  #inside here, have to refer to objects as list elements
  #the command print(m) searches through environments to find an object
  #if nothing exists locally, m will find myenv$m, but is misleading
  #try(print(m))  
  #now create a local object m that is different
  m <- "blah"
  print(m) #gives 'blah'
  print(myenv$m)
  
  #return at end as a list
  invisible(as.list(myenv))
 
}  
inside_function <- function(env) {
  #create/overwrite objects in env
  
  env$m <- matrix(stats::runif(4), ncol=2)
  #these are created in real time within inside_function without having
  #to return env (notice NULL is a returned value)
  print(env$m)
  #overwite
  env$m <- matrix(stats::runif(4), ncol=2)
  print(env$m)
  env$d <- 5
  print(env$d)
  env$d <- env$d + runif(1)
  env$z <- letters[sample(1:20, size=6)]
  invisible(NULL)
}

tmp <- outside_function()
print(tmp) #contains all the objects as a list

【讨论】:

    猜你喜欢
    • 2014-06-21
    • 2011-12-27
    • 2015-11-21
    • 2015-08-02
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多