R中包中的全局变量

时间:2012-09-26 09:09:37

标签: r global-variables

我正在用R开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量?

我已经阅读了一些关于环境的内容,但我不明白它是如何工作的,如果这就是解决问题的方法。

6 个答案:

答案 0 :(得分:49)

您可以在环境中使用包本地变量。这些变量可用于包中的多个功能,但不能(轻松)访问用户,并且不会干扰用户工作空间。一个简单快速的例子是:

pkg.env <- new.env()

pkg.env$cur.val <- 0
pkg.env$times.changed <- 0

inc <- function(by=1) {
    pkg.env$times.changed <- pkg.env$times.changed + 1
    pkg.env$cur.val <- pkg.env$cur.val + by
    pkg.env$cur.val
}

dec <- function(by=1) {
    pkg.env$times.changed <- pkg.env$times.changed + 1
    pkg.env$cur.val <- pkg.env$cur.val - by
    pkg.env$cur.val
}

cur <- function(){
    cat('the current value is', pkg.env$cur.val, 'and it has been changed', 
        pkg.env$times.changed, 'times\n')
}

inc()
inc()
inc(5)
dec()
dec(2)
inc()
cur()

答案 1 :(得分:16)

您可以设置option,例如

options("mypkg-myval"=3)
1+getOption("mypkg-myval")
[1] 4

答案 2 :(得分:9)

通常,全局变量是 evil 。它们为邪恶的根本原则是您希望最小化包中的互连。这些互连通常会导致函数产生副作用,即它不仅取决于输入参数的结果,还取决于某些全局变量的值。特别是当函数数量增加时,这很难得到正确的调试。

对于R中的全局变量,请参阅此SO post

编辑以回复您的评论: 另一种方法是将所需信息传递给需要它的函数。您可以创建一个包含以下信息的新对象:

token_information = list(token1 = "087091287129387",
                         token2 = "UA2329723")

并要求所有需要此信息的函数将其作为参数:

do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)

通过这种方式,从代码中可以清楚地看到函数中需要令牌信息,您可以自己调试函数。此外,该函数没有副作用,因为它的行为完全由其输入参数决定。典型的用户脚本如下所示:

token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)

我希望这会让事情更清楚。

答案 3 :(得分:3)

问题不明确:

  • 只需一个R流程或几个?

  • 只是在一台主机上,还是在多台机器上?

  • 他们之间是否有共同的文件访问权限?

按照复杂性的递增顺序,我会使用一个文件,一个SQLite后端通过RSQlite包或(我最喜欢的)rredis包来设置/读取Redis实例。

答案 4 :(得分:0)

您还可以创建令牌列表,并使用usethis::use_data(..., internal = TRUE)将其添加到R / sysdata.rda。该文件中的数据是内部数据,但所有功能均可访问。如果您只希望 some 函数访问令牌,就会出现唯一的问题,这可以通过以下方式更好地解决:

    上面已经提出的环境解决方案;或
  1. 创建一个隐藏的帮助器函数来保存令牌并返回它们。然后,只需在使用令牌的函数中调用此隐藏函数,即可(假设它是一个列表),您可以使用list2env(..., envir = environment())将它们注入到其环境中。

答案 5 :(得分:0)

如果您不介意向程序包添加依赖项,则可以使用homonym package中的R6对象,如对@ greg-snow的答案的注释中所建议的。

R6对象是可以添加公共方法和私有方法的实际环境,非常轻巧,可以作为共享软件包的全局变量的一种好方法,而且更加严格,而不会污染全局环境。

与@ greg-snow的解决方案相比,它可以更严格地控​​制变量(例如,您可以添加检查类型的方法)。缺点可能是依赖,当然还有学习R6语法。

library(R6)
MyPkgOptions = R6::R6Class(
  "mypkg_options",
  public = list(
    get_option = function(x) private$.options[[x]]
  ),
  active = list(
    var1 = function(x){
      if(missing(x)) private$.options[['var1']]
      else stop("This is an environment parameter that cannot be changed")
    }
    ,var2 = function(x){
      if(missing(x)) private$.options[['var2']]
      else stop("This is an environment parameter that cannot be changed")
    }
  ),
  private = list(
    .options = list(
      var1 = 1,
      var2 = 2
    )
  )
)
# Create an instance
mypkg_options = MyPkgOptions$new()
# Fetch values from active fields
mypkg_options$var1
#> [1] 1
mypkg_options$var2
#> [1] 2
# Alternative way
mypkg_options$get_option("var1")
#> [1] 1
mypkg_options$get_option("var3")
#> NULL
# Variables are locked unless you add a method to change them
mypkg_options$var1 = 3
#> Error in (function (x) : This is an environment parameter that cannot be changed

reprex package(v0.3.0)于2020-05-27创建