我正在用 R 开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量?
我读过一些关于环境的东西,但我不明白它是如何工作的,如果这甚至是处理这些事情的方式。
我正在用 R 开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量?
我读过一些关于环境的东西,但我不明白它是如何工作的,如果这甚至是处理这些事情的方式。
您可以通过环境使用包局部变量。这些变量将可用于包中的多个函数,但用户无法(轻松)访问,并且不会干扰用户的工作空间。一个快速简单的例子是:
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()
你可以设置一个option
,例如
options("mypkg-myval"=3)
1+getOption("mypkg-myval")
[1] 4
一般来说,全局变量是邪恶的。它们邪恶的根本原理是您希望尽量减少包中的互连。这些互连通常会导致函数产生副作用,即它不仅取决于输入参数的结果是什么,还取决于某些全局变量的值。尤其是当函数的数量增加时,这可能很难正确进行调试。
对于 R 中的全局变量,请参阅此SO 帖子。
根据您的评论进行编辑: 另一种方法是将所需信息传递给需要它的功能。您可以创建一个包含此信息的新对象:
token_information = list(token1 = "087091287129387",
token2 = "UA2329723")
并要求所有需要此信息的函数将其作为参数:
do_stuff = function(arg1, arg2, token)
do_stuff(arg1, arg2, token = token_information)
这样从代码中就可以看出函数中需要token信息,可以自行调试函数。此外,该函数没有副作用,因为它的行为完全由它的输入参数决定。典型的用户脚本如下所示:
token_info = create_token(token1, token2)
do_stuff(arg1, arg2, token_info)
我希望这能让事情更清楚。
您还可以创建一个令牌列表并将其添加到 R/sysdata.rda 中usethis::use_data(..., internal = TRUE)
。此文件中的数据是内部数据,但所有函数均可访问。如果您只希望某些功能访问令牌,则会出现唯一的问题,这将更好地服务于:
list2env(..., envir = environment())
。如果您不介意向您的包添加依赖项,您可以使用同名包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 包(v0.3.0)于 2020-05-27 创建