2014-06-29 90 views
3

這裏有四個功能,後者包裝前者。爲什麼包裝函數不能按預期工作?

a <- 0 

f1 <- function(expr) { 
    a1 <- 1 
    eval(expr) 
} 

f2 <- function(expr) { 
    a2 <- 2 
    f1(expr) 
} 

f3 <- function(expr) { 
    a3 <- 3 
    f2(expr) 
} 

f4 <- function(expr) { 
    a4 <- 4 
    f3(expr) 
} 

執行以下操作experienments:

> f4(a) 
0 

預期其工作。但是,如果我們稱之爲

F4(A4) 錯誤的eval(表達式):對象 'A4' 未找到

> f4(a3) 
Error in eval(expr) : object 'a3' not found 

... 

> f2(a2) 
Error in eval(expr) : object 'a2' not found 

> f2(a1) 
Error in eval(expr) : object 'a1' not found 

> f1(a1) 
Error in eval(expr) : object 'a1' not found 

我檢查每一個函數體f3的當地環境和父母的環境的父母框架是f4的本地環境,...,f1的父母是f2的身體。這是爲什麼發生這種情況的明確解釋?我怎樣才能擺脫這個問題,使代碼工作的目的是函數調用應該允許後續函數(如f3)找到已定義的符號(例如a4)?

回答

8

我強烈建議你花一些時間閱讀Advanced R: Environments

首先,當我運行f1(a1)時,我也得到了「object'a1'not found」;不是「1」,因爲你在上面。

問題是,默認情況下R使用函數的封閉環境解析變量。函數的封閉環境在函數被定義時決定,而不是函數被調用。因此它不會上傳調用鏈來解析變量名稱。您可以明確地查看具有parent.frame()環境的調用父級,但這些環境在嵌套函數調用中不會鏈接在一起。

就像get()通過在封閉的父環境中行走來循環變量一樣,您可以使自己的函數遍歷調用環境並查看哪些變量可用。

call.get <- function(val) { 
    for(i in 1:sys.nframe()) { 
     if (exists(val, envir=sys.frame(i), inherits=F)) { 
      return(get(val, envir=sys.frame(i))) 
     } 
    } 
    return(NULL) 
} 

call.ls <- function(val) { 
    vars<-lapply(1:sys.nframe(), function(i) ls(envir=parent.frame(i))) 
    return(sort(unique(unlist(vars)))) 
} 

然後,如果你這樣做

f1 <- function(expr) { 
    a1 <- 1 
    call.ls() 
} 

f2 <- function(expr) { 
    a2 <- 2 
    f1(expr) 
} 

f3 <- function(expr) { 
    a3 <- 3 
    f2(expr) 
} 

f4 <- function(expr) { 
    a4 <- 4 
    f3(expr) 
} 

f4(1) 

你會得到

"a1" "a2" "a3" "expr" "FUN" "val" "X" 

,你可以使用

call.get("a3") 

得到這些變量中的一個從父呼叫幀。

但您遇到的另一個問題是您在調用子函數時觸發expr參數的評估。當你做

f2 <- function(expr) { 
    a2 <- 2 
    f1(expr) 
} 

,用於評估在f2環境expr和結果傳遞給f1。那時你正在失去評估。通過懶惰評估的最簡單方法是使用「...」。像

f1 <- function(...) { 
    a1 <- 1 
    expr<-deparse(substitute(...)) 
    call.get(expr) 
} 
f2 <- function(...) { 
    a2 <- 2 
    f1(...) 
} 
f2(a1) 
# [1] 1 
f2(a2) 
# [1] 2 

東西否則,你需要用do.call

f1 <- function(expr) { 
    a1 <- 1 
    expr<-deparse(substitute(expr)) 
    call.get(expr) 
} 
f2 <- function(expr) { 
    expr<-substitute(expr) 
    a2 <- 2 
    do.call(f1, list(expr)) 
} 

f2(a1) 
# [1] 1 
f2(a2) 
# [1] 2 
+0

由於更明確地傳遞表達爲你詳細的解釋!我想我應該再讀一遍這一章。 –

+2

@KunRen如果你發現這回答你的問題,你應該點擊這個答案旁邊的複選標記接受它並關閉問題。 – MrFlick

相關問題