R 環境和變數作用域的問題

2021-06-26 14:20:34 字數 2581 閱讀 6244

在r語言的文獻中,函式被證實地稱為「閉包(closure)」。函式不僅包括引數和函式體,也包括它的「環境(environment)」。環境是由建立函式時出現的物件集構成。

►頂層環境

> w <- 12> f <- function( y )

+     return ( h() )

+ }> environment( f )

此例中,函式f()是在頂層(直譯器命令提示符下)構建的,於是它處於頂層環境。頂層環境在r的輸出結果裡表示為r_globalenv,不過,它常與r**的.globalenv混淆。如果以批處理方式執行r程式,也會被認為在頂層。

函式ls()會把某個環境中的所有物件列舉出來。如果,在頂層呼叫它,就會得到頂層的環境下的物件名單。用ls.str()可以獲得更多的資訊。

在函式中呼叫不帶引數的ls()會返回當前的區域性變數(包括引數)。使用envir引數,ls會輸出函式呼叫鏈中任何乙個框架的區域性變數名。

►變數作用域的層次

在r語言中,因為函式屬於物件,在函式中定義乙個函式是可以實現的,有時從物件導向程式設計封裝的目的來看,這樣做也是可取的。我們可以在任何地方都可以建立物件。

正如變數d一樣,h()屬於f()的區域性物件。在這種情況下,變數作用域的層次問題是有意義的。根據r語言的定義,f()的區域性變數d,相對於h()來說是區域性變數。對於引數y,結論也一樣,因為在r語言中,引數也被看作區域性變數。類似的,變數作用域的分層特性意味著,既然w是f()的全域性變數,那麼它也是h()的全域性變數。實際上,我們在函式h()中也用到了w。

就環境而言,h()的環境包括在它建立時定義的所有物件,也就是,如果f()被呼叫多次,h()也多次建立,但它又會隨著f()的返回而消失。h()對f()來說是區域性的,且在頂層環境中不可見。

在層次中發生命名衝突時可以的(儘管這樣做並不可取),在這種情況下,優先使用最裡層的變數。像這種以繼承的方式建立的環境一般靠它們的記憶體位址來引用。

> f <- function( y )

> h <- function()

> f( 1 )

error in fun(left, right) : non-numeric argument to binary operator

這樣並不可行,由於h()定義於頂層,變數d不再在h()的環境中,於是,**執行出錯。更糟糕的是,如果碰巧在頂層環境下存在乙個不相關的變數d,我們不會得到出錯提示,只會返回乙個錯誤的結果。

你也許會想到為什麼上例中r沒有提示變數y不存在。這是因為前面提到過的惰性求值原則,即只有當需要某個變數值的時候,r才會計算它。在這個例子中,r已經遇到了乙個由d引起的錯誤而不會繼續求y的值。

以上**的修正方法是把d和y設定為引數:

> f <- function( y )

> h <- function( dd, yy )

> f( 1 )

[1] 104

下面的**:

> w <- 2

> f <- function( y, hh )

> h <- function( dd, yy )

> f( 1, h )

[1] 24

在f()執行時,形式引數dd與實際引數d相匹配。由於引數被看作區域性變數,你也許猜測dd的環境與頂層環境不一樣。但閉包包括了環境,所以dd與h的環境一致。

►函式(幾乎)沒有***

函式式程式設計哲學的另乙個特徵是,函式不會修改非區域性變數。也就是說,一般情況下,使用函式時不會有***。函式的**可以讀,但不能寫其非區域性變數。**看似可以給這些變數重新複製,但實際上這種行為只會影響它們的備份,而不是變數本身。 

注:r對任何函式的呼叫都會建立乙個框架(frame)。該框架包括函式中建立的區域性變數,以及在乙個環境求值時組合建立的新環境。

> w <- 12

> f <- function( y )

+     return ( h() )

+ }> t <- 4

> f( t )

[1] 13

[1] 120

> w

[1] 12

> t

[1] 4

所以,頂層的w並沒有改變,即使看起來在f()中被修改了。只有f()中w的區域性被跟發生了改變。類似地,頂層的變數t也沒有改變,儘管與它相關聯的引數y發生了變化。實際上,區域性變數w與相應的全域性變數共享乙個記憶體位址,直到區域性變數的數值發生了變化。這種情況下,會分配給區域性變數w新的記憶體位址。

►關於全域性變數

r語言中全域性變數的使用比想象中的要多。r語言自身內部大量使用了全域性變數,物理上其c語言**還是r例程,對於這點你也許會感到驚訝。例如,超賦值運算子<<-用於許多r語言函式中(儘管通常只對上一級環境層次中的變數進行寫操作)。執行緒化(threaded)**和gpu**傾向於大量使用全域性變數,這為並行物件提供了主要的通訊途徑。兩者均用於編寫高效能程式。

►關於「閉包」

r語言中「閉包」包含了函式的引數、函式體以及呼叫時的環境。有一種程式設計方法是用閉包包括環境,這種程式設計方法使用的特性也叫做「閉包」。

閉包包含了乙個可建立區域性變數的函式,並建立另乙個函式可以訪問該變數。

R語言變數作用域

語言採用的是lexical scoping 詞法作用域 詞法作用域,又稱靜態作用域,即變數定義後的作用域是不變的。在函式的主體裡面,變數是分為被約束的 區域性的 或自由的三種。被約束的變數是指匹配函式形式引數的變數。區域性變數是指,在函式主題內建立和定義的變數。自由變數指的是那些既不是 區域性也不是...

變數 作用域和記憶體問題

1.1 動態的屬性 引用型別 var person new object person.name nicholas alert person.name nicholas 基本型別 var name nicholas name.age 27 alert name.age undefined1.2 複製...

變數作用域問題

2018 04 09 首先一定要明白乙個問題,那就是你py程式裡面的變數作用域問題。因為你程式是工作在乙個分布式機器上的,那麼這些個資料就會分布在不同的機器上,或者說container上。要明確spark的工作架構,各個不同的工作身份。那麼乙個變數他是不是在不同的機器執行,他是不是最後會彙總到我們的...