2011-06-23 62 views
83

我已经创建了一个实用程序R脚本util.R,我想从我的项目中的其他脚本使用该脚本。 确保此脚本定义的函数可用于其他脚本中的函数的正确方法是什么?如何在其他脚本中包含(源)R脚本

我正在寻找类似于require函数的东西,该函数仅在尚未加载程序包的情况下才加载程序包。我不想调用source("util.R"),因为每次调用它时都会加载脚本。

我知道我会得到一些答案,告诉我要创建一个包,如Organizing R Source Code :) 但是我没有创建将在别处使用的东西,它只是一个独立的项目。

+31

我一直为独立项目创建软件包。这不是多少工作,好处是巨大的。继续吧,你知道你想这么做...... – Andrie

回答

73

这是一种可能的方式。使用exists函数检查util.R代码中的唯一内容。

例如:

if(!exists("foo", mode="function")) source("util.R") 

(编辑,包括mode="function",如加文·辛普森指出)

+4

不错的使用'exists()' - 需要'mode =“function”'加入以使其更加傻瓜 –

+1

exists()似乎会抛出一个错误,除了在R 3.0.2中返回一个错误。 –

+0

正确的用法是exists(“foo”),并且编辑了答案。 – Andrie

8

util.R产生函数foo()。您可以检查此功能是在全球环境中可用,源脚本,如果它不是:

if(identical(length(ls(pattern = "^foo$")), 0)) 
    source("util.R") 

这将找到名为foo什么。如果你想找到一个函数,那么(正如@Andrie所提到的)exists()是有帮助的,但是需要准确地知道要查找什么类型的对象,例如,

if(exists("foo", mode = "function")) 
    source("util.R") 

这里是行动exists()

> exists("foo", mode = "function") 
[1] FALSE 
> foo <- function(x) x 
> exists("foo", mode = "function") 
[1] TRUE 
> rm(foo) 
> foo <- 1:10 
> exists("foo", mode = "function") 
[1] FALSE 
+0

在这种情况下,你可能想使用'grepl(...,value = TRUE)',因为你的搜索词可能不是正则表达式。 +1,顺便说一句。 – Andrie

+0

?? 'grepl()'没有参数'value',但我应该在'ls()'中修正正则表达式... ... –

+0

对不起,我的错误。我的意思是'fixed = TRUE' – Andrie

15

有内置的没有这样的事情,因为R不跟踪调用source和无法弄清楚什么是从哪里加载(当使用包时,情况并非如此)。然而,你可以使用同样的想法在C .h文件,即整个包住在:

if(!exists('util_R')){ 
util_R<-T 

#Code 

} 
+1

+1击败我32秒... – Andrie

+0

,然后在'if'代码中调用'source(“util.R”)',对吧? – rafalotufo

+1

@rafalotufo您将照常(“util.R”)来源。 mbq文章中的代码将会转化为* util.R.你只需将util.R中的全部内容放入一个巨大的if()语句中,如果这样做有道理的话。 –

4

你可以写一个函数,它接受一个文件名和环境名称,检查是否该文件已被加载到环境中,如果不是,则使用sys.source来源文件。

这里有一个快速的和未经考验的功能(改善欢迎!):

include <- function(file, env) { 
    # ensure file and env are provided 
    if(missing(file) || missing(env)) 
    stop("'file' and 'env' must be provided") 
    # ensure env is character 
    if(!is.character(file) || !is.character(env)) 
    stop("'file' and 'env' must be a character") 

    # see if env is attached to the search path 
    if(env %in% search()) { 
    ENV <- get(env) 
    files <- get(".files",ENV) 
    # if the file hasn't been loaded 
    if(!(file %in% files)) { 
     sys.source(file, ENV)      # load the file 
     assign(".files", c(file, files), envir=ENV) # set the flag 
    } 
    } else { 
    ENV <- attach(NULL, name=env)  # create/attach new environment 
    sys.source(file, ENV)    # load the file 
    assign(".files", file, envir=ENV) # set the flag 
    } 
} 
3

这里是我写的一个功能。它包装了base::source函数以在名为sourced的全局环境列表中存储源文件的列表。如果您为调用源提供.force=TRUE参数,它将仅重新源文件。它的参数签名与真实的source()相同,所以你不需要重写你的脚本来使用它。

warning("overriding source with my own function FYI") 
source <- function(path, .force=FALSE, ...) { 
    library(tools) 
    path <- tryCatch(normalizePath(path), error=function(e) path) 
    m<-md5sum(path) 

    go<-TRUE 
    if (!is.vector(.GlobalEnv$sourced)) { 
    .GlobalEnv$sourced <- list() 
    } 
    if(! is.null(.GlobalEnv$sourced[[path]])) { 
    if(m == .GlobalEnv$sourced[[path]]) { 
     message(sprintf("Not re-sourcing %s. Override with:\n source('%s', .force=TRUE)", path, path)) 
     go<-FALSE 
    } 
    else { 
     message(sprintf('re-sourcing %s as it has changed from: %s to: %s', path, .GlobalEnv$sourced[[path]], m)) 
     go<-TRUE 
    } 
    } 
    if(.force) { 
    go<-TRUE 
    message(" ...forcing.") 
    } 
    if(go) { 
    message(sprintf("sourcing %s", path)) 
    .GlobalEnv$sourced[path] <- m 
    base::source(path, ...) 
    } 
} 

这是很健谈的(大量调用message()的),所以如果你关心你可以把那些行了。任何意见来自经验丰富的R用户表示赞赏;我对R很新。