R:解开范围

res*_*dsk 5 scope r

我的问题是在R中编写模块时避免命名空间污染.

现在,在我的R项目,我functions1.RdoFoo()doBar(),functions2.R与其他功能,并且main.R在它的主程序,它首先做source('functions1.R'); source('functions2.R'),然后调用等功能.

我一直在Mac OS X的R GUI中启动程序source('main.R').这是第一次没问题,但在那之后,第一次通过程序定义的变量第二次functions*.R被定义,因此函数得到了一大堆额外的变量.

我不想要那个!当我的函数使用它不应该的变量时,我想要一个"未定义的变量"错误!这两次给了我很晚的调试!

那么其他人如何处理这类问题呢?是否有类似的东西source(),但这会产生一个独立的命名空间,而不是主要的命名空间?制作一个包似乎是一个解决方案,但与Python相比,它似乎是一个很大的痛苦,因为源文件自动是一个单独的命名空间.

有小费吗?谢谢!

Ian*_*ows 5

我会探讨两种可能的解决方案.

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)


hat*_*rix 4

您要使用的主要函数是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)

当然,如果您的项目变得很大并且有很多很多的函数(和变量),那么编写一个包是最好的选择。