R中包中的全局变量

R中包中的全局变量,r,global-variables,R,Global Variables,我正在用R开发一个包。我有一堆函数,其中一些需要一些全局变量。如何管理包中的全局变量 我读过一些关于环境的书,但我不明白它是如何工作的,甚至不知道这是否是解决问题的方法。一般来说,全局变量是邪恶的。它们之所以邪恶的根本原则是,您希望最小化包中的互连。这些相互关联通常会导致函数产生副作用,即它不仅取决于输入参数以及结果,还取决于某些全局变量的值。特别是当函数的数量增加时,这可能很难正确执行,而且很难调试 对于R中的全局变量,请参见此 根据您的评论进行编辑: 另一种方法是将所需信息传递给需要它的函数

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


我读过一些关于环境的书,但我不明白它是如何工作的,甚至不知道这是否是解决问题的方法。

一般来说,全局变量是邪恶的。它们之所以邪恶的根本原则是,您希望最小化包中的互连。这些相互关联通常会导致函数产生副作用,即它不仅取决于输入参数以及结果,还取决于某些全局变量的值。特别是当函数的数量增加时,这可能很难正确执行,而且很难调试

对于R中的全局变量,请参见此

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

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)

我希望这能让事情变得更清楚。

你可以设置一个
选项,例如

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

问题不清楚:

  • 只是一个R进程还是几个

  • 仅在一台主机上,还是跨多台计算机

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


随着复杂性的增加,我会使用一个文件,通过包或(我最喜欢的:)包的SQLite后端来设置/读取Redis实例。

您可以通过环境使用包局部变量。这些变量将可用于包中的多个函数,但用户无法(轻松)访问,并且不会干扰用户工作区。一个简单的例子是:

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()

pkg.env您还可以创建令牌列表,并使用
usethis::use_data(…,internal=TRUE)
将其添加到R/sysdata.rda。此文件中的数据是内部数据,但可由所有函数访问。如果您只想让某些函数访问令牌,则会出现唯一的问题,最好通过以下方式解决:

  • 上述已提出的环境解决方案;或
  • 创建一个隐藏的helper函数来保存令牌并返回它们。然后,只需在使用令牌的函数中调用这个隐藏函数(假设它是一个列表),就可以使用
    list2env(…,envir=environment())
    将它们注入到它们的环境中

  • 如果您不介意向包中添加依赖项,那么可以使用来自的
    R6
    对象,正如@greg snow的答案中的注释所建议的那样

    R6
    对象是可以添加公共和私有方法的实际环境,非常轻量级,是共享包的全局变量的一个好的、更严格的选项,而不会污染全局环境

    与@gregsnow的解决方案相比,它允许对变量进行更严格的控制(例如,您可以添加检查类型的方法)。缺点可能是依赖性,当然还有学习
    R6
    语法

    库(R6)
    MyPkgOptions=R6::R6Class(
    “mypkg_选项”,
    公共=列表(
    get_option=函数(x)private$.options[[x]]
    ),
    活动=列表(
    var1=函数(x){
    if(缺少(x))private$.options[['var1']]
    else stop(“这是一个无法更改的环境参数”)
    }
    ,var2=函数(x){
    if(缺少(x))private$.options[['var2']]
    else stop(“这是一个无法更改的环境参数”)
    }
    ),
    私有=列表(
    .options=列表(
    var1=1,
    var2=2
    )
    )
    )
    #创建一个实例
    mypkg_options=mypkg选项$new()
    #从活动字段获取值
    mypkg_选项$var1
    #> [1] 1
    mypkg_选项$var2
    #> [1] 2
    #替代方法
    mypkg_选项$get_选项(“var1”)
    #> [1] 1
    mypkg_选项$get_选项(“var3”)
    #>空的
    #变量将被锁定,除非您添加了更改它们的方法
    mypkg_选项$var1=3
    #>(函数(x)中出错:这是一个无法更改的环境参数
    

    由(v0.3.0)于2020年5月27日创建

    您能详细说明您的具体情况吗?然后我们可以帮助您找到替代方案,最好是……谢谢您的回答。我有编程经验,并且知道全局变量通常是无效的。但是,我正在建立对服务的API访问,为了保持与此服务的连接,函数需要一些这些令牌应该是所有函数都可以访问的,我想到的是创建一个.RData文件来存储这些数据,但这似乎是一个糟糕的想法。正常的R模式是使用某种“handle”对象来保存令牌,并将该句柄传递给函数。这也允许您有多个并发会话使用不同的标记。例如,这就是数据库访问的模式。我认为你关于为什么全局变量是邪恶的论点需要对R进行一些调整-你在包中创建的所有函数都是全局变量。它们是邪恶的吗?;)所有全局变量都是邪恶的,但有些比其他变量更邪恶;)。引用类似乎是一种更为经典的面向对象方法。这将允许对象方法(函数)也是本地的。这将存储在哪里?@Rimbaud在名为
    的pairlist中。Options
    位于
    base
    包中。它存储在加载包的R会话的全局选项列表中。请参见
    ?选项
    。这是一个有用的实践,我想补充一点,作为将环境创建为可变容器时的安全措施,一个