第六輪迴 全局配置

第六輪迴 全局配置

異教徒被囚禁於第六輪迴的燃燒墳墓中.

一個全局配置可以通過”<<-“實現:

> x <- 1
> y <- 2
> fun
function () 
{
 x <- 101
 y <<- 102
}
> fun()
> x
[1] 1
> y
[1] 102  

但這就像是在火山口生活一樣.

如果你需要使用“<<-“,請三思.如果你深思熟慮之後還是覺得需要使用“<<-“,請再三思.只有當你的老闆因爲你的不作爲而生氣的紅了臉的時候,你也應該只是暫時性的使用“<<-“.早已經有提議(並非半開玩笑的)從R中消除“<<-“,但這並不意味着就清除了全局配置,只是強迫你使用assign()去實現相同的目的.

全局配置到底哪裏錯了呢? Surprise.

Surprise在電影和小說中是美好的.但是在程序中就不那麼討人喜歡了.

在R中,除了少數幾個函數明確的有負作用(即對函數之外的環境造成影響)之外,其餘的函數我們都期望是沒有副作用的.函數中如果使用了全局配置,那就是與這種期望公然對抗.對於不熟悉這些代碼的用戶(甚至是幾周前編寫這些代碼的作者),這裏將會有一個變量在魔術般地變化.

全局配置的一個有用的(並不是極壞的)特殊場合是記憶.具體講就是:程序的運行結果可以被存儲起來,當後期再有相同的計算的時候,這個結果就可以僅僅從存儲中找到而不是再次計算.全局配置在這種場合並不會令人擔心,因爲這個對用戶並不可見.這也有一個命名銷燬的問題–如果你在兩個不同的函數中採用相同名字的變量去記憶結果,銷燬就隨之而來.

在R中,我們可以通過一個局部全局變量來實現記憶.(“局部全局”挺起來有點幽默,但是它卻簡潔地描述了到底是怎麼一回事.)在下面計算斐波那契數列的例子中,我們使用了“<<-“,並且安全地使用.

fibonacci <- local({
                     memo <- c(1, 1, rep(NA, 100))
                     f <- function(x) 
                     {
                       if(x == 0) return(0)
                       if(x < 0) return(NA)
                       if(x > length(memo))
                       stop("'x' too big for implementation")
                       if(!is.na(memo[x])) return(memo[x])
                       ans <- f(x-2) + f(x-1)
                       memo[x] <<- ans
                       ans
                     }
                    })  

糊塗神是怎麼說的?我們有一個僅僅通過“<<-“操作的本地化方式來實現記憶的函數.但是我們再該函數的本地環境中存放備忘記錄.返回值就是大括號最後的那個值.當我們定義函數的時候一般不會對返回對象命名,但是在這種情況下我們需要命名因爲這個函數需要遞歸調用.

現在我們玩玩看:

> fibonacci(4)
[1] 3
> head(get(’memo’, envir=environment(fibonacci)))
[1] 1 1 2 3 NA NA   

通過計算入參是4的Fibonacci(),memo的第三個和第四個函數被填充進來.這些值將不會被重複計算,需要用的時候僅僅是查表就可以了.

R總是進行着值傳遞.它從不會進行引用傳遞.

在R中有兩種人:一種是理解上邊所我所說的,一種不是.

如果你不理解這些的話,R是很適合你的–意思是R對你來說是安全的(儘管本章都在說一些相反的話題).f翻譯成人話就是說在R中讓數據”腐爛”是極度困難的.畢竟智者知無止境.

如果你真的知道這一輪迴我們所討論的問題,那麼你或許早就領悟到了R是深受函數式編程影響的–負作用被降到最低.或許你會擔心這預示着內存的嚴重低效使用.好吧,這輪迴我們討論的問題就是一個謊言.如果這只是字面上的正確,當對象(或許非常大)是函數入參時總會被複制.事實上,R努力去僅僅複製那些必須使用的對象,比如說當這個對象在函數裏邊發生變化的時候.這個輪迴是大致概念上的正確,而非字裏行間的正確.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章