我正在用R开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量?
我已经阅读了一些关于环境的内容,但我不明白它是如何工作的,如果这就是解决问题的方法。
答案 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 函数访问令牌,就会出现唯一的问题,这可以通过以下方式更好地解决:
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创建