2013-04-21 81 views
57

我正在閱讀Hadley Wickhams在Github上的書,特別是this part on lazy evaluation。在那裏他給出了懶惰評價的後果的一個例子,在add/adders函數的部分。讓我引用這一點:你所說的加法器 之一解釋一個懶惰的評價怪癖

add <- function(x) { 
    function(y) x + y 
} 
adders <- lapply(1:10, add) 
adders[[1]](10) 
adders[[10]](10) 

x被懶洋洋地評估的第一次:

這[懶評測]創建與lapply關閉或循環時是非常重要的功能。在這一點上,循環完成,並且最終的值爲10。因此,所有的加法器函數都會在它們的 輸入上加10,可能不是你想要的!手動強制評估修復 問題:

add <- function(x) { 
    force(x) 
    function(y) x + y 
} 
adders2 <- lapply(1:10, add) 
adders2[[1]](10) 
adders2[[10]](10) 

我似乎不明白位,並且說明有最小。有人可以詳細說明這個特定的例子,並解釋發生了什麼?我特別困惑於這句話:「在這一點上,循環完成,x的最終值爲10」。什麼循環?什麼終極價值在哪裏?一定是我想念的東西,但我只是沒有看到它。提前致謝。

+3

注意的是,這個問題的答案已經改變爲R 3.2.0,請參閱我的回答如下。 – jhin 2015-06-08 09:09:20

+0

對@ jhin的評論的補充:儘管'lapply()'在最近的R中已經發生了變化,但是旨在用於lapply()的任何地方的函數'purrr :: map()'仍然像舊的' lapply()'相對於閉包的共享環境。然而,我不會指望'purrr :: map()'這個「不合時宜」,因爲它可能會在未來的版本中被糾正。 – egnha 2016-10-21 16:31:28

+0

@jhin其實,我猜哈德利的教程是直接從github構建的,所以在R 3.2.0之後閱讀它現在非常怪異,因爲該版本在整個教程中對懶惰評估進行了部分介紹:與加法器沒有多大區別, adders2的輸出! – 2016-12-23 23:13:55

回答

34

的目標:

adders <- lapply(1:10, function(x) add(x)) 

是創建的add功能列表,所述第一加1到其輸入端,第二加2等惰性求使R鍵等待真正創建加法器直到你真的開始調用函數。問題是在創建第一個加法函數後,xlapply循環增加,結束於10的值。當您調用第一個加法函數時,懶惰評估現在生成函數,得到值x。的問題是,原始x不再等於一,但在lapply循環結束時的值,即10。

因此,惰性計算導致所有加法器的功能要等到lapply循環完成後在真正構建功能。然後他們用相同的值建立它們的函數,即10. Hadley建議的解決方案是直接評估x,避免延遲評估,並用正確的x值得到正確的函數。

+4

好的,讓我重新說明一下,看看我是否正確。當我們調用'lapply'時,R類記住所有10個加法函數的結構,但是尚未評估x。當我們調用第一個加法函數時,R表示,aha,讓我們看看它是什麼,取x,在lapply調用中已經是10,並且將第一個被調用的加法函數評估爲10 + y。對於其餘的加法器函數來說,它們都是相同的。可能粗暴地說,但是它的邏輯呢? – 2013-04-21 10:26:39

+0

我相信這是事實。 – 2013-04-21 11:10:57

+1

@ Maxim.K是的,這是完全正確的。 – hadley 2013-04-21 13:18:27

52

從R 3.2.0起,這不再是真的!

change log相應行讀取:

高階函數,如應用的功能和減少()現在 力參數它們以消除延遲計算和之間 不必要的相互作用應用功能可變捕獲 關閉。

事實上:

add <- function(x) { 
    function(y) x + y 
} 
adders <- lapply(1:10, add) 
adders[[1]](10) 
# [1] 11 
adders[[10]](10) 
# [1] 20 
+1

了不起的信息。我也困惑了,而且我認爲這可能是樂透了 – AllYouCanEat86 2016-01-11 20:48:18