2011-06-23 17 views
83

He creado un script R de utilidad, util.R, que quiero usar desde otros scripts en mi proyecto. ¿Cuál es la forma correcta de garantizar que la función que define este script esté disponible para funcionar en mis otros scripts?Cómo incluir (fuente) el script R en otros scripts

Estoy buscando algo similar a la función require, que carga un paquete solo si aún no se ha cargado. No quiero llamar al source("util.R") porque eso cargará el script cada vez que se llame.

Sé que obtendré algunas respuestas diciéndome que cree un paquete, como en Organizing R Source Code :) Pero no crearé algo que se use en otro lugar, es solo un proyecto independiente.

+31

puedo crear paquetes para proyectos independientes todo el tiempo. No es mucho trabajo, y los beneficios son enormes. Vamos, sabes que quieres hacerlo ... – Andrie

Respuesta

73

Aquí es una forma posible. Use la función exists para buscar algo único en su código util.R.

Por ejemplo:

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

(Editado para incluir mode="function", como Gavin Simpson señaló)

+4

Niza uso de 'existe()' - 'necesita mode = "función"' añadir para que sea a prueba de tontos –

+1

'existe()' parece arrojar un error excepción de entrega de uno de R 3.0.2. –

+0

El uso correcto es 'existe (" foo ") y la respuesta fue editada. – Andrie

8

Diga util.R produce una función foo(). Puede comprobar si esta función está disponible en el entorno global y la fuente de la secuencia de comandos si no lo es:

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

que encontrará cualquier cosa con el nombre foo. Si desea encontrar una función, entonces (como lo menciona @Andrie) exists() es útil, pero necesita que se le diga exactamente qué tipo de objeto buscar, p.

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

Aquí es exists() en acción:

> 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

En este caso, es posible que desee utilizar 'Grepl (..., valor = VERDADERO)' debido a que su término de búsqueda no es probablemente una expresión regular. +1, por cierto. – Andrie

+0

?? 'Grepl()' no tiene un argumento 'valor', pero probablemente debería fijar la expresión regular en' ls() '... –

+0

Lo siento, mi error. Quise decir 'fijo = VERDADERO' – Andrie

15

No hay tal cosa incorporado, puesto que R no rastrea las llamadas a source y no es capaz de averiguar lo que estaba cargado desde donde (este no es el caso cuando se usan paquetes). Sin embargo, es posible que utilice misma idea que en C .h archivos, es decir, envuelve el conjunto en:

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

#Code 

} 
+1

1 me ganó por 32 segundos ... – Andrie

+0

y luego llaman 'fuente ("util.R")' 'dentro del código if', ¿verdad? – rafalotufo

+1

@rafalotufo Deberías buscar ("util.R") como de costumbre. El código en la publicación de mbq iría * a * util.R. Simplemente pones todo el cuerpo de lo que está en uso. Ahora mismo en una declaración if() gigante, si tiene sentido. –

4

Se puede escribir una función que toma un nombre de archivo y un nombre de entorno, comprueba si el archivo se ha cargado en el entorno y usa sys.source para generar el archivo si no es así.

Aquí es una función rápida y no probado (mejoras dan la bienvenida!):

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

Aquí es una función que escribí. Envuelve la función base::source para almacenar una lista de archivos de origen en una lista de entorno global denominada sourced. Solo volverá a generar un archivo si proporciona un argumento .force=TRUE a la llamada al origen. Su firma de argumento es, por lo demás, idéntica a la source() real, por lo que no es necesario volver a escribir las secuencias de comandos para usar esto.

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

Es bastante hablador (un montón de llamadas a message()) para que pueda llevar a cabo esas líneas si se preocupan. Cualquier consejo de veteranos usuarios de R es apreciado; Soy bastante nuevo para R.

Cuestiones relacionadas