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


108

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

我正在寻找类似于该require功能的内容,即仅在尚未加载程序包时才加载该程序包。我不想调用,source("util.R")因为这将在每次调用脚本时加载该脚本。

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


37
我一直在为独立项目创建软件包。它工作量不大,而且收益巨大。继续,您知道您想这样做...
Andrie

Answers:


93

这是一种可能的方法。使用该exists功能检查util.R代码中是否有独特的内容。

例如:

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

mode="function"如Gavin Simpson所指出,被编辑为包括在内)


4
很好的使用exists()-需要mode = "function"添加以使其变得更加简单
Gavin Simpson

1
exists()除了在R 3.0.2中返回一个错误外,似乎抛出一个错误。
Michael Schubert

正确的用法是`exists(“ foo”)并且答案已被编辑。
安德里

18

没有内置的东西,因为R不会跟踪对调用的调用source,也无法弄清楚从何处加载了什么(使用包时不是这种情况)。但是,您可能会使用与C .h文件中相同的想法,即将整个文件包装在:

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

 #Code

}

然后source("util.R")if代码内调用,对吗?
rafalotufo 2011年

1
@rafalotufo您将像往常一样使用source(“ util.R”)。mbq帖子中的代码将放入 util.R。您只要将util.R中的整个内容放入一个巨大的if()语句中(如果可以的话)。
基思·特姆布雷

10

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

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

?? grepl()没有论据value,但我可能应该修复正则表达式中ls()...
Gavin Simpson

抱歉,是我的错。我的意思是fixed=TRUE
Andrie

@Andrie-啊,好的。反正还是行不通的。在思考的同时被拖走了。exists()更好,但现在我看到您同时发布了这样的答案。
加文·辛普森

5

您可以编写一个使用文件名和环境名称的函数,检查文件是否已加载到环境中,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
  }
}

5

这是我编写的函数。它包装了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的新手。


0

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

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

后:

if(!exists("foo", mode="function")) source("C:/tests/utils.r")
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.