2013-08-20 24 views
6

我正在編寫一些用於執行重複任務的函數,但我試圖最小化加載數據的次數。基本上我有一個函數需要一些信息並繪製一個圖表。然後我有第二個函數將循環並輸出多個圖.pdf。在這兩種功能我有下面的代碼行:嵌套函數環境選擇

if(load.dat) load("myworkspace.RData") 

其中load.dat是邏輯和我所需要的數據存儲在myworkspace.RData。當我調用循環並輸出多個圖的包裝函數時,我不想在每次調用內部函數時重新加載工作區。我以爲我可以在包裝函數中加載一次工作空間,然後內部函數可以訪問這些數據,但是我得到了一個錯誤,說明了其他情況。

所以我的理解是當一個函數在其本地環境中找不到該變量(在函數被調用時創建)時,該函數將查找該變量的父環境。

我認爲內部函數調用的父環境是外部函數調用。顯然,這是不正確的:

func1 <- function(...){ 
    print(var1) 
} 

func2 <- function(...){ 
    var1 <- "hello" 
    func1(...) 
} 

> func2() 
Error in print(var1) : object 'var1' not found 

閱讀了許多問題,語言的手冊,並this真正有用的博客文章後,我想出了以下內容:

var1 <- "hello" 
save(list="var1",file="test.RData") 
rm(var1) 

func3 <- function(...){ 
    attach("test.RData") 
    func1(...) 
    detach("file:test.RData") 
} 

> func3() 
[1] "hello" 

有沒有更好的辦法做到這個?爲什麼func1func2創建的本地環境中查找未定義的變量,當時它是func2,稱爲func1

注:我不知道如何命名這個問題。如果有人有更好的建議,我會改變它並編輯這一行。

+2

詞法作用域表示該功能將查找在其父的環境,這是不一定調用環境未定義的符號。檢查這也是:https://github.com/hadley/devtools/wiki/Environments –

+0

@ Ferdinand.kraft感謝您的鏈接。今天下午我會解決這個問題。 – dayne

+0

如果你的數據是以數據框的形式存在,你可以使用'data.table'包,並將你的表作爲參數傳遞給'func3'裏面的'func1'。此軟件包僅供參考,不會產生您的數據不需要的副本。 –

回答

7

爲了說明詞彙範圍,考慮以下因素:

首先讓我們創建一個沙盒環境,才避免了哦,所以,普通R_GlobalEnv:

sandbox <-new.env() 

現在我們在其中放入兩個函數:f,它尋找一個名爲x的變量;和g,它定義了一個局部x並調用f

sandbox$f <- function() 
{ 
    value <- if(exists("x")) x else "not found." 
    cat("This is function f looking for symbol x:", value, "\n") 
} 

sandbox$g <- function() 
{ 
    x <- 123 
    cat("This is function g. ") 
    f() 
} 

技術性:在控制檯中輸入函數定義會導致再有封閉的環境設置爲R_GlobalEnv,所以我們手動強制的fg機箱搭配他們所屬的環境:

environment(sandbox$f) <- sandbox 
environment(sandbox$g) <- sandbox 

調用g。局部變量x=123不受f發現:

> sandbox$g() 
This is function g. This is function f looking for symbol x: not found. 

現在我們在全球環境中創建x並調用g。該功能f將尋找x首先在沙箱中,然後在沙箱中的父,這恰好是R_GlobalEnv:

> x <- 456 
> sandbox$g() 
This is function g. This is function f looking for symbol x: 456 

只是爲了檢查f尋找x首先在它的外殼,我們可以把一個x有和呼叫g

> sandbox$x <- 789 
> sandbox$g() 
This is function g. This is function f looking for symbol x: 789 

結論:R中符號查找如下嵌套函數的執行期間創建的封閉環境中的鏈,評估幀調用。

編輯:只要添加一個鏈接this very interesting answer from Martin Morgan on the related subject of parent.frame() vs parent.env()

+0

這是我見過的最好的插圖。非常感謝!我並不真正瞭解環境和框架的差異。 – dayne

2

你可以使用閉:

f2 <- function(...){ 
    f1 <- function(...){ 
    print(var1) 
    } 
    var1 <- "hello" 
    f1(...) 
} 
f2() 
+0

沒錯,但我需要能夠使用內部函數作爲獨立功能。我不想每次調用外部函數時重新定義內部函數(更不用說重複一堆代碼)。 – dayne

+0

然後在我看來最乾淨的設置:把所有的數據放在一個列表(my_data)中,然後把它作爲你函數的參數。可以使用函數(my_data,{})來避免額外的輸入。 –