2011-06-23 21 views
83

Stworzyłem skrypt R narzędzia util.R, którego chcę używać z innych skryptów w moim projekcie. Jaki jest właściwy sposób na zapewnienie, że funkcja, którą ten skrypt definiuje, może działać w innych moich skryptach?Jak dodać skrypt (źródło) R do innych skryptów

Szukam czegoś podobnego do funkcji require, która ładuje pakiet tylko wtedy, gdy jeszcze go nie załadowano. Nie chcę wywoływać source("util.R"), ponieważ spowoduje to załadowanie skryptu za każdym razem, gdy zostanie wywołany.

Wiem, że otrzymam kilka odpowiedzi mówiących mi o utworzeniu paczki, jak w Organizing R Source Code :) Ale nie tworzę czegoś, co będzie używane gdzie indziej, to tylko samodzielny projekt.

+31

Często tworzę pakiety dla samodzielnych projektów. To niewiele pracy, a korzyści są ogromne. No dalej, wiesz, że chcesz to zrobić ... – Andrie

Odpowiedz

73

Oto jeden z możliwych sposobów. Użyj funkcji exists, aby sprawdzić, co jest unikatowe w kodzie util.R.

Na przykład:

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

(Zmieniano zawierać mode="function", jak Gavin Simpson wskazał)

+4

Dobre wykorzystanie 'exist()' - needs 'mode =" function "' dodawanie, aby uczynić go bezbłędnym –

+1

'exists()' wydaje się zgłaszać błąd, z wyjątkiem zwrócenia go w wersji 3.0.2. –

+0

Poprawne użycie to "exists (" foo "), a odpowiedź została poddana edycji. – Andrie

8

Powiedz util.R produkuje funkcję foo(). Można sprawdzić, czy funkcja ta jest dostępna w globalnym środowisku oraz źródła skryptu, jeśli nie jest:

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

To będzie znaleźć coś z nazwą foo. Jeśli chcesz znaleźć funkcję, to (jak wspomina @Andrie) exists() jest pomocna, ale trzeba mu powiedzieć dokładnie, jakiego typu obiektu szukać, np.

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

Oto exists() w akcji:

> 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

W tym przypadku możesz użyć 'grepl (..., value = TRUE)', ponieważ wyszukiwany termin prawdopodobnie nie jest wyrażeniem regularnym. +1, przy okazji. – Andrie

+0

?? 'grepl()' nie ma argumentu 'value', ale prawdopodobnie powinienem naprawić wyrażenie regularne w' ls() '... –

+0

Przepraszam, mój błąd. Miałem na myśli 'fixed = TRUE' – Andrie

15

Nie ma czegoś takiego, wbudowany, ponieważ R nie zapisuje rozmowy, aby source i nie jest w stanie zorientować się, co zostało załadowane skąd (tak nie jest w przypadku korzystania z pakietów). Mimo to, można użyć tego samego pojęcia jak w C .h plików, tj zawinąć całość w:

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

#Code 

} 
+1

+1 pokonać mnie przez 32 sekundy ... – Andrie

+0

, a następnie wywołać 'source (" util.R ")' w ramach kodu 'if', prawda? – rafalotufo

+1

@rafalotufo Będziesz jak zwykle źródłem ("util.R"). Kod w posta mbq trafi * na * util.R. Po prostu wstawiasz całe ciało tego, co jest w użyciu.R teraz w gigantyczną instrukcję if(), jeśli to ma sens. –

4

można napisać funkcję, która pobiera nazwę pliku i nazwę środowiskową, sprawdza, czy plik został załadowany do środowiska i używa sys.source do źródła pliku, jeśli nie.

Oto szybki i niesprawdzone function (ulepszeń mile widziane!):

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

Oto funkcja pisałem. Zawijanie funkcji base::source do przechowywania listy plików źródłowych na liście globalnych środowisk o nazwie sourced. Będzie on ponownie źródłem pliku tylko wtedy, gdy podasz argument .force=TRUE do wywołania źródłowego. Podpis jego argumentu jest identyczny z prawdziwym numerem source(), więc nie musisz przepisywać skryptów, aby z niego korzystać.

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

To bardzo rozmowny (wiele połączeń do message()), więc można wziąć te linie, jeśli dbasz. Wszelkie porady od doświadczonych użytkowników R są doceniane; Jestem całkiem nowy dla R.

Powiązane problemy