2016-09-21 101 views
4

是否有办法通过R中的脚本source()使其作为父级附加到全局环境(.GlobalEnv)?在R中分离环境的源脚本,而不是在全球环境中

当前,当我编写脚本时,该脚本的所有变量和函数都会出现在我的全局(交互)环境中。我想将这些变量和函数包含在搜索路径中,但不包含在.GlobalEnv中。也就是说,我想被执行的脚本的行为类似于附接的封装,它获取全球和碱环境之间连接(参见从图Advanced REnvironments

enter image description here

+2

只需构建一个包? – Roland

+0

我想避免在这个特定的实例中构建一个包 – Megatron

回答

2

下列环境插入出现以实现所需的功能,但是,我不知道这是做到这一点的最好办法:

检查当前的搜索路径:

search() 
# [1] ".GlobalEnv"  "package:stats"  "package:graphics" 
# [4] "package:grDevices" "package:utils"  "package:datasets" 
# [7] "package:methods" "Autoloads"   "package:base" 

添加新的环境,源包和使用local参数时source() ING:

attach(new.env(), name="sourced_scripts") 
myEnv <- as.environment("sourced_scripts") 

source("some_other_script.R", local=myEnv) 

search() 
# [1] ".GlobalEnv"  "package:dplyr"  "sourced_scripts" 
# [4] "package:stats"  "package:graphics" "package:grDevices" 
# [7] "package:utils"  "package:datasets" "package:methods" 
# [10] "Autoloads"   "package:base" 

我们的脚本中加入了dplyr包的搜索路径,但要注意"package:dplyr"环境之前执行的脚本环境。

为了使源代码函数能够使用dplyr(以及任何其他包),我们删除"sourced_script"环境并将其重新附加到搜索路径的前端,位于源脚本附加的包之前。 注意:使用attach()来执行此操作将不起作用,因为attach()会插入输入环境的副本(在本例中为myEnv)。

detach("sourced_scripts") 
parent.env(myEnv) <- parent.env(.GlobalEnv) 
parent.env(.GlobalEnv) <- myEnv 
rm(myEnv) # at this point we can remove myEnv to clear up namespace 

search() 
# [1] ".GlobalEnv"  "sourced_scripts" "package:dplyr" 
# [4] "package:stats"  "package:graphics" "package:grDevices" 
# [7] "package:utils"  "package:datasets" "package:methods" 
# [10] "Autoloads"   "package:base" 
3

source文档中,local参数可以是用于确定其中源表达式进行求值的环境。

这表明您可以创建一个新环境,运行source,将此环境传递给local,然后将attach环境转到搜索路径。

或者你可以使用与what=NULL连接创建一个空的环境,保存返回值,并传递到localsource

tmp <- attach(what=NULL) 
source('test.R', local=tmp) 

或为单行:

source('test.R', local=attach(NULL)) 
+1

该方法的一个问题是插入了源脚本环境,并且脚本中附加的任何库都是子级 - 所以它找不到库 – Megatron

2

将脚本作为源代码包发送的最简单方法是(即,在调用R脚本中定义的函数时,lexical scoping不会导致使用全局环境中定义的变量)是创建一个e该环境的父母是.BaseNamespaceEnv,然后使用该环境拨打source()

例如,如果你有一个这样的脚本:

# << my-script.R >> 
my_fun <- function(x){x + y} 

然后评估在控制台下,不会产生一个错误,因为它会如果my_fun被它自己的包中定义:

source("my-script.R") 
y = 2 
my_fun(1) 
#> 3 

但是,如果你创建,其search()路径不包括全球环境(.GlobalEnv)的环境中,那么你就当你从你的脚本中调用该函数得到适当的错误:

# Create the environment: 
ENV = new.env(parent = .BaseNamespaceEnv) 
# Attache it to the search path so that objects in your environment can be 
# found from the global environment (i.e. from the console): 
attach(ENV) 
# do things: 
source("my-script.R",ENV) 
y = 2 
my_fun(1) 
#> Error in .ENV$my_fun(3) : object 'y' not found 
+0

好的答案(I我也在寻找一个解决方案),但是用'library'加载到源文件中的包的功能仍然不可访问,因为它们在搜索路径中作为环境ENV的父级附加。试试这个代码库(data.table);打印(搜索()); x“ - as.data.table(mtcars)'在”my-script.R“中,你会得到错误”Error in'[.data.frame'(x,i,j):object'cyl'not发现“来源时,你描述它。原因是源文件中的“错误的”搜索路径:'[1]“.GlobalEnv”“package:data.table”“ENV”...' –

+0

实际上,你永远不会从内部调用'library()一个R包。你可以在'DESCRIPTION'文件的'imports'部分列出* your *软件包依赖的软件包,R会在加载软件包之前加载这些软件包。这里相当于调用'library(data.table)',然后使用'ENV = new.env(parent = as.environment(“package:data.table”))''。 – Jthorpe

+0

是的,你是绝对正确的,在一个包里没有'library'。我正在寻找解决方案的原因是我正在开发的“批处理作业框架包”,并且会在外部环境中尽可能多地分离出实际工作和源代码的外部代码。我只注入“作业上下文”数据,源代码甚至不知道框架中的“作业上下文”接口。我知道我也可以定义必须实现的功能接口,但我希望尽可能让用户尽可能简单... –