设置放在.GlobalEnv

时间:2016-12-14 02:01:04

标签: r module scope

我希望将自定义环境中的函数附加到全局环境,同时屏蔽可能的内部函数。

具体来说,假设f()使用内部函数g(),然后:

  1. f()不应在.GlobalEnv ls(all=TRUE)
  2. 中显示
  3. f()应该可以从.GlobalEnv使用。
  4. f()内部函数g()应该 不可见
  5. 首先让我们按如下方式创建环境和功能:

    .GlobalEnv

    我现在应该跑吗:

    assign('ep', value=new.env(parent=.BaseNamespaceEnv), envir=.BaseNamespaceEnv)
    assign('e', value=new.env(parent=ep), envir=ep)
    assign('g', value=function() print('hello'), envir=ep)
    assign('f', value=function() g(), envir=ep$e)
    
    ls(.GlobalEnv)
    ## character(0)
    

    事实上,ep$e$f() ## Error in ep$e$f() (from #1) : could not find function "g" 的调用环境是:

    f

    其中environment(get('f', envir=ep$e)) ## <environment: R_GlobalEnv> 不存在。

    尝试更改g的环境会出错:

    f

    显然它适用于:

    environment(get('f', envir=ep$e))=ep
    ## Error in environment(get("f", envir = ep$e)) = ep : 
    ##   target of assignment expands to non-language object
    

    现在,根据需要,environment(ep$e$f)=ep attach(ep$e) 只能使用f().GlobalEnv不可以。

    g()

    此外,f() [1] "hello" g() ## Error: could not find function "g" (intended behaviour) f()g()都不可见,但遗憾的是:

    .GlobalEnv

    将与ls(.GlobalEnv) ## [1] "ep" 相关联的环境设置为f(),将ep放入ep
    混乱全球环境正是我试图避免的 我是否可以重置.GlobalEnv的父环境,而不会从全局环境中看到它?

    更新
    根据您的反馈,您建议构建一个包以获得适当的命名空间服务 包裹不灵活。我的帮助函数存储在项目子目录中,比如f,并且源自hlp
    通过这种方式,可以在项目的基础上轻松地混合和更新脚本。

    (在上面添加了新的枚举列表)

    更新2

    一个几乎完整的解决方案,不需要外部包,现在是here

2 个答案:

答案 0 :(得分:1)

首先,你不应该搞乱基本命名空间。因为你不想让全球环境变得混乱而使基地变得混乱只是愚蠢。*

其次,您可以将local()用作穷人的命名空间:

e <- local({
    g <- function() "hello"
    f <- function() g()
    environment()
})

e$f()
# [1] "hello"

*如果您想到的是一种存储包状态的方法,请记住(基本上)您在全局环境中放置的任何内容在打包时都会放在自己的命名空间中。所以不要担心事情的混乱。

答案 1 :(得分:1)

包或modules完全符合您的要求。如果你对软件包缺乏灵活性感到不满意,我建议你给一个镜头:它们优雅地解决你的问题并允许你将任意R源文件视为模块:

只需在“隐藏”函数前加一个点(g.g 1 。将这些定义写入文件foo.r,然后通过

加载
foo = import('foo')
foo$f()

import_package('foo', attach = TRUE)
f()

这符合您枚举中的所有要点。特别是,这两段代码使f,而不是.g可供调用者使用。此外,模块还有numerous other advantages over using source

在技术性更强的说明中,您的代码会导致ep进入全局环境,因为作业environment(ep$e$f)=ep会在您的全局范围内创建ep副本环境。连接环境后,可以删除此对象。但是,代码仍有问题(它比必要的更复杂,正如Hong Ooi所提到的,你不应该搞乱基本命名空间)。

1 目前,这是模块发出未导出对象信号的方式。未来版本的模块将允许另一种不需要调整对象名称的机制。