如何在其他脚本中包含(源) R 脚本

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

I'm looking for something similar to the require function, that loads a package only if it has not been loaded yet. I don't want to call source("util.R") because that will load the script every time it is called.

我知道我会得到一些答案,告诉我创建一个包,如在 组织 R 源代码:) 但是我并没有创建一些可以在其他地方使用的东西,它只是一个独立的项目。

145576 次浏览

假设 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

There is no such thing built-in, since R does not track calls to source and is not able to figure out what was loaded from where (this is not the case when using packages). Yet, you may use same idea as in C .h files, i.e. wrap the whole in:

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


#Code


}

这里有一个可能的方法。使用 exists函数来检查您的 util.R代码中的某些独特内容。

For example:

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

(Edited to include mode="function", as Gavin Simpson pointed out)

You could write a function that takes a filename and an environment name, checks to see if the file has been loaded into the environment and uses sys.source to source the file if not.

这里有一个快速且未经测试的功能(欢迎改进!) :

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
}
}

这是我写的一个函数。它包装了 base::source函数,将源文件列表存储在一个名为 sourced的全局环境列表中。只有在向 source 调用提供 .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 还是个新手。

我用我的代码所在的整个地址解决了我的问题: 以前:

if(!exists("foo", mode="function")) source("utils.r")

之后:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")