rafalotufo
rafalotufo

Reputation: 3932

How to include (source) R script in other scripts

I've created a utility R script, util.R, which I want to use from other scripts in my project. What is the proper way to ensure that the function this script defines are available to function in my other scripts?

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.

I know that I will get some answers telling me to create a package, as in Organizing R Source Code :) But I'm not creating something that will be used elsewhere, it is just a standalone project.

Upvotes: 124

Views: 157776

Answers (6)

I solved my problem using entire address where my code is: Before:

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

After:

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

Upvotes: 0

Andrie
Andrie

Reputation: 179398

Here is one possible way. Use the exists function to check for something unique in your util.R code.

For example:

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

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

Upvotes: 107

Keith Twombley
Keith Twombley

Reputation: 1688

Here is a function I wrote. It wraps the base::source function to store a list of sourced files in a global environment list named sourced. It will only re-source a file if you provide a .force=TRUE argument to the call to source. Its argument signature is otherwise identical to the real source() so you don't need to rewrite your scripts to use this.

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, ...)
  }
}

It's pretty chatty (lots of calls to message()) so you can take those lines out if you care. Any advice from veteran R users is appreciated; I'm pretty new to R.

Upvotes: 5

Joshua Ulrich
Joshua Ulrich

Reputation: 176638

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.

Here's a quick and untested function (improvements welcome!):

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

Upvotes: 5

Gavin Simpson
Gavin Simpson

Reputation: 174788

Say util.R produces a function foo(). You can check if this function is available in the global environment and source the script if it isn't:

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

That will find anything with the name foo. If you want to find a function, then (as mentioned by @Andrie) exists() is helpful but needs to be told exactly what type of object to look for, e.g.

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

Here is exists() in action:

> 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

Upvotes: 10

mbq
mbq

Reputation: 18628

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

}

Upvotes: 18

Related Questions