【问题标题】:Set the environment of a function placed outside the .GlobalEnv设置放置在 .GlobalEnv 之外的函数的环境
【发布时间】:2016-12-14 02:01:03
【问题描述】:

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

具体来说,说f()使用了一个内部函数g(),那么:

  1. f() 不应该在 .GlobalEnvls(all=TRUE) 中可见。
  2. f() 应该可以从 .GlobalEnv 使用。
  3. f() 内部函数 g() 应该可见并且可从.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,来源类似于 source("hlp/util1.R")
通过这种方式,脚本可以很容易地在项目的基础上进行混合和更新。

(在顶部添加了新的枚举列表)

更新 2

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

【问题讨论】:

  • “混乱的全球环境正是我想要避免的。”只需构建一个包。
  • @Roland:请看更新版本。

标签: r module scope


【解决方案1】:

无论是包还是 modules 都可以满足您的需求。如果您对软件包缺乏灵活性不满意,我建议您试一试“box”模块:它们优雅地解决了您的问题,并允许您将任意 R 源文件视为模块:

只需在模块内用注释#' @export标记公共函数,然后通过

box::use(./foo)
foo$f()

box::use(./foo[...])
f()

这满足了您列举的所有要点。特别是,这两段代码使调用者可以使用f,但不能使用g。另外,模块有numerous other advantages over using source

从更专业的角度来说,您的代码导致ep 位于全局环境中,因为赋值environment(ep$e$f)=ep 在您的全局环境中创建了ep副本。附加环境后,您可以删除此对象。但是,代码仍然存在问题(它比必要的复杂,而且正如 Hong Ooi 所提到的,您不应该弄乱基本命名空间)。

【讨论】:

  • 非常有趣的方法。不是我打算弄乱基本命名空间,但我怎样才能创建一个 new.env() 以便它位于全局命名空间之外?
  • @antonio 正如我在回答中简要提到的,您可以attach 环境,然后rm 引用它。这可以在全局命名空间中完成。
【解决方案2】:

首先,您不应该乱用基本命名空间。因为你不想弄乱全球环境而弄乱基础是愚蠢的。*

其次,你可以使用local()作为穷人的命名空间:

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

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

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

【讨论】:

  • 有理由将fg 包含在单独的环境中:仅附加f 环境并仅使f 可在全局环境中使用(请参阅更新的问题)。关于弄脏基本环境,我如何创建一个不显示 ls(all=TRUE) 的环境?
  • local 不做任何命名空间,它只是让 OP 的赋值代码更容易编写……
猜你喜欢
  • 1970-01-01
  • 2014-07-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-07-02
  • 2012-04-19
相关资源
最近更新 更多