2011-06-23 97 views
83

我已經創建了一個實用程序R腳本util.R,我想從我的項目中的其他腳本使用該腳本。 確保此腳本定義的函數可用於其他腳本中的函數的正確方法是什麼?如何在其他腳本中包含(源)R腳本

我正在尋找類似於require函數的東西,該函數僅在尚未加載程序包的情況下才加載程序包。我不想調用source("util.R"),因爲每次調用它時都會加載腳本。

我知道我會得到一些答案,告訴我要創建一個包,如Organizing R Source Code :) 但是我沒有創建將在別處使用的東西,它只是一個獨立的項目。

+31

我一直爲獨立項目創建軟件包。這不是多少工作,好處是巨大的。繼續吧,你知道你想這麼做...... – Andrie

回答

73

這是一種可能的方式。使用exists函數檢查util.R代碼中的唯一內容。

例如:

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

(編輯,包括mode="function",如加文·辛普森指出)

+4

不錯的使用'exists()' - 需要'mode =「function」'加入以使其更加傻瓜 –

+1

exists()似乎會拋出一個錯誤,除了在R 3.0.2中返回一個錯誤。 –

+0

正確的用法是exists(「foo」),並且編輯了答案。 – Andrie

8

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 
+0

在這種情況下,你可能想使用'grepl(...,value = TRUE)',因爲你的搜索詞可能不是正則表達式。 +1,順便說一句。 – Andrie

+0

?? 'grepl()'沒有參數'value',但我應該在'ls()'中修正正則表達式... ... –

+0

對不起,我的錯誤。我的意思是'fixed = TRUE' – Andrie

15

有內置的沒有這樣的事情,因爲R不跟蹤調用source和無法弄清楚什麼是從哪裏加載(當使用包時,情況並非如此)。然而,你可以使用同樣的想法在C .h文件,即整個包住在:

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

#Code 

} 
+1

+1擊敗我32秒... – Andrie

+0

,然後在'if'代碼中調用'source(「util.R」)',對吧? – rafalotufo

+1

@rafalotufo您將照常(「util.R」)來源。 mbq文章中的代碼將會轉化爲* util.R.你只需將util.R中的全部內容放入一個巨大的if()語句中,如果這樣做有道理的話。 –

4

你可以寫一個函數,它接受一個文件名和環境名稱,檢查是否該文件已被加載到環境中,如果不是,則使用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 
    } 
} 
3

這裏是我寫的一個功能。它包裝了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很新。