2016-09-21 105 views
4

是否有辦法通過R中的腳本source()使其作爲父級附加到全局環境(.GlobalEnv)?在R中分離環境的源腳本,而不是在全球環境中

當前,當我編寫腳本時,該腳本的所有變量和函數都會出現在我的全局(交互)環境中。我想將這些變量和函數包含在搜索路徑中,但不包含在.GlobalEnv中。也就是說,我想被執行的腳本的行爲類似於附接的封裝,它獲取全球和鹼環境之間連接(參見從圖Advanced REnvironments

enter image description here

+2

只需構建一個包? – Roland

+0

我想避免在這個特定的實例中構建一個包 – Megatron

回答

2

下列環境插入出現以實現所需的功能,但是,我不知道這是做到這一點的最好辦法:

檢查當前的搜索路徑:

search() 
# [1] ".GlobalEnv"  "package:stats"  "package:graphics" 
# [4] "package:grDevices" "package:utils"  "package:datasets" 
# [7] "package:methods" "Autoloads"   "package:base" 

添加新的環境,源包和使用local參數時source() ING:

attach(new.env(), name="sourced_scripts") 
myEnv <- as.environment("sourced_scripts") 

source("some_other_script.R", local=myEnv) 

search() 
# [1] ".GlobalEnv"  "package:dplyr"  "sourced_scripts" 
# [4] "package:stats"  "package:graphics" "package:grDevices" 
# [7] "package:utils"  "package:datasets" "package:methods" 
# [10] "Autoloads"   "package:base" 

我們的腳本中加入了dplyr包的搜索路徑,但要注意"package:dplyr"環境之前執行的腳本環境。

爲了使源代碼函數能夠使用dplyr(以及任何其他包),我們刪除"sourced_script"環境並將其重新附加到搜索路徑的前端,位於源腳本附加的包之前。 注意:使用attach()來執行此操作將不起作用,因爲attach()會插入輸入環境的副本(在本例中爲myEnv)。

detach("sourced_scripts") 
parent.env(myEnv) <- parent.env(.GlobalEnv) 
parent.env(.GlobalEnv) <- myEnv 
rm(myEnv) # at this point we can remove myEnv to clear up namespace 

search() 
# [1] ".GlobalEnv"  "sourced_scripts" "package:dplyr" 
# [4] "package:stats"  "package:graphics" "package:grDevices" 
# [7] "package:utils"  "package:datasets" "package:methods" 
# [10] "Autoloads"   "package:base" 
3

source文檔中,local參數可以是用於確定其中源表達式進行求值的環境。

這表明您可以創建一個新環境,運行source,將此環境傳遞給local,然後將attach環境轉到搜索路徑。

或者你可以使用與what=NULL連接創建一個空的環境,保存返回值,並傳遞到localsource

tmp <- attach(what=NULL) 
source('test.R', local=tmp) 

或爲單行:

source('test.R', local=attach(NULL)) 
+1

該方法的一個問題是插入了源腳本環境,並且腳本中附加的任何庫都是子級 - 所以它找不到庫 – Megatron

2

將腳本作爲源代碼包發送的最簡單方法是(即,在調用R腳本中定義的函數時,lexical scoping不會導致使用全局環境中定義的變量)是創建一個e該環境的父母是.BaseNamespaceEnv,然後使用該環境撥打source()

例如,如果你有一個這樣的腳本:

# << my-script.R >> 
my_fun <- function(x){x + y} 

然後評估在控制檯下,不會產生一個錯誤,因爲它會如果my_fun被它自己的包中定義:

source("my-script.R") 
y = 2 
my_fun(1) 
#> 3 

但是,如果你創建,其search()路徑不包括全球環境(.GlobalEnv)的環境中,那麼你就當你從你的腳本中調用該函數得到適當的錯誤:

# Create the environment: 
ENV = new.env(parent = .BaseNamespaceEnv) 
# Attache it to the search path so that objects in your environment can be 
# found from the global environment (i.e. from the console): 
attach(ENV) 
# do things: 
source("my-script.R",ENV) 
y = 2 
my_fun(1) 
#> Error in .ENV$my_fun(3) : object 'y' not found 
+0

好的答案(I我也在尋找一個解決方案),但是用'library'加載到源文件中的包的功能仍然不可訪問,因爲它們在搜索路徑中作爲環境ENV的父級附加。試試這個代碼庫(data.table);打印(搜索()); x「 - as.data.table(mtcars)'在」my-script.R「中,你會得到錯誤」Error in'[.data.frame'(x,i,j):object'cyl'not發現「來源時,你描述它。原因是源文件中的「錯誤的」搜索路徑:'[1]「.GlobalEnv」「package:data.table」「ENV」...' –

+0

實際上,你永遠不會從內部調用'library()一個R包。你可以在'DESCRIPTION'文件的'imports'部分列出* your *軟件包依賴的軟件包,R會在加載軟件包之前加載這些軟件包。這裏相當於調用'library(data.table)',然後使用'ENV = new.env(parent = as.environment(「package:data.table」))''。 – Jthorpe

+0

是的,你是絕對正確的,在一個包裏沒有'library'。我正在尋找解決方案的原因是我正在開發的「批處理作業框架包」,並且會在外部環境中儘可能多地分離出實際工作和源代碼的外部代碼。我只注入「作業上下文」數據,源代碼甚至不知道框架中的「作業上下文」接口。我知道我也可以定義必須實現的功能接口,但我希望儘可能讓用戶儘可能簡單... –