我的问题是在R中编写模块时避免命名空间污染.
现在,在我的R项目,我functions1.R有doFoo()和doBar(),functions2.R与其他功能,并且main.R在它的主程序,它首先做source('functions1.R'); source('functions2.R'),然后调用等功能.
我一直在Mac OS X的R GUI中启动程序source('main.R').这是第一次没问题,但在那之后,第一次通过程序定义的变量第二次functions*.R被定义,因此函数得到了一大堆额外的变量.
我不想要那个!当我的函数使用它不应该的变量时,我想要一个"未定义的变量"错误!这两次给了我很晚的调试!
那么其他人如何处理这类问题呢?是否有类似的东西source(),但这会产生一个独立的命名空间,而不是主要的命名空间?制作一个包似乎是一个解决方案,但与Python相比,它似乎是一个很大的痛苦,因为源文件自动是一个单独的命名空间.
有小费吗?谢谢!
我会探讨两种可能的解决方案.
a)以更实用的方式思考更多.不要在函数之外创建任何变量.所以,例如,main.R应该包含一个函数main(),它在其他文件中提供,并完成工作.当主要回归时,没有任何杂乱的东西会留下来.
b)手动清理:
#main.R
prior_variables <- ls()
source('functions1.R')
source('functions2.R')
#stuff happens
rm(list = setdiff(ls(),prior_variables))`
Run Code Online (Sandbox Code Playgroud)
您要使用的主要函数是sys.source(),它将在全局名称空间之外的名称空间(R 中的“环境”)中加载您的函数/变量。在 R 中您可以做的另一件很棒的事情是将命名空间附加到您的search()路径,这样您就不需要直接引用命名空间。也就是说,如果“namespace1”在您的搜索路径上,则其中的函数(例如“fun1”)不需要像namespace1.fun1()Python 中那样调用,而是像fun1(). [方法解析顺序:] 如果有多个同名函数,search()则将调用环境中列表中第一个出现的函数。要显式调用特定命名空间中的函数,许多可能的语法之一(尽管有点难看)get("fun1","namespace1")(...)是. 这也应该适用于变量,使用语法。我一直这样做(我通常只加载函数,但 R 中函数和变量之间的区别很小),所以我编写了一些从我的....fun1()get("var1","namespace1")~/.Rprofile
name.to.env <- function(env.name)
## returns named environment on search() path
pos.to.env(grep(env.name,search()))
attach.env <- function(env.name)
## creates and attaches environment to search path if it doesn't already exist
if( all(regexpr(env.name,search())<0) ) attach(NULL,name=env.name,pos=2)
populate.env <- function(env.name,path,...) {
## populates environment with functions in file or directory
## creates and attaches named environment to search() path
## if it doesn't already exist
attach.env(env.name)
if( file.info(path[1])$isdir )
lapply(list.files(path,full.names=TRUE,...),
sys.source,name.to.env(env.name)) else
lapply(path,sys.source,name.to.env(env.name))
invisible()
}
Run Code Online (Sandbox Code Playgroud)
用法示例:
populate.env("fun1","pathtofile/functions1.R")
populate.env("fun2","pathtofile/functions2.R")
Run Code Online (Sandbox Code Playgroud)
依此类推,这将创建两个独立的命名空间:“fun1”和“fun2”,它们附加到路径(在这种情况下,search()“fun2”将在列表中更高)。search()这类似于做类似的事情
attach(NULL,name="fun1")
sys.source("pathtofile/functions1.R",pos.to.env(2))
Run Code Online (Sandbox Code Playgroud)
手动为每个文件(“2”是路径上的默认位置search())。编写方式populate.env(),如果一个目录(例如“functions/”)包含许多 R 文件且函数名称不冲突,则可以将其称为
populate.env("myfunctions","functions/")
Run Code Online (Sandbox Code Playgroud)
将所有函数(和变量)加载到单个命名空间中。有了name.to.env(),你还可以做类似的事情
with(name.to.env("fun1"), doStuff(var1))
Run Code Online (Sandbox Code Playgroud)
或者
evalq(doStuff(var1), name.to.env("fun1"))
Run Code Online (Sandbox Code Playgroud)
当然,如果您的项目变得很大并且有很多很多的函数(和变量),那么编写一个包是最好的选择。