2013-04-21 13 views
57

Sto leggendo il libro di Hadley Wickhams su Github, in particolare this part on lazy evaluation. Lì fornisce un esempio di conseguenze della valutazione lazy, nella parte con le funzioni add/adders. Permettetemi di citare quel po ':Spiegare una piccola curiosità di valutazione

Questo [valutazione pigra] è importante per la creazione di chiusure con lapply o un ciclo:

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

x è pigramente valutata la prima volta che si chiama uno dei vipera funzioni. A questo punto, il ciclo è completo e il valore finale di x è 10. Quindi tutte le funzioni di aggiunta aggiungeranno 10 al loro input , probabilmente non quello che volevi! costringendo manualmente correzioni di valutazione il problema:

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

non mi sembra di capire che po ', e la spiegazione non v'è il minimo. Qualcuno potrebbe per favore elaborare quel particolare esempio e spiegare cosa succede lì? Sono particolarmente perplesso dalla frase "a questo punto, il ciclo è completo e il valore finale di x è 10". Quale ciclo? Quale valore finale, dove? Deve essere qualcosa di semplice che mi manca, ma semplicemente non lo vedo. Grazie mille in anticipo.

+3

Nota che la risposta a questa domanda è cambiata di R 3.2.0, vedere la mia risposta qui sotto. – jhin

+0

Complementare al commento di @jhin: Mentre 'lapply()' è cambiato nella recente R, la funzione 'purrr :: map()', che deve essere usata ovunque 'lapply()', si comporta ancora come la vecchia ' lapply() 'vis-à-vis con ambienti condivisi di chiusure. Tuttavia, non conterei su questo "anacronismo" di 'purrr :: map()' da aggirare, poiché sarà probabilmente corretto nelle versioni future. – egnha

+0

@jhin In realtà, immagino che il tutorial di hadley sia stato creato direttamente da github in modo da leggerlo dopo che la R 3.2.0 è abbastanza bizzarra dato che questa versione ha reso l'intera sezione sulla valutazione lazy in quel tutorial moot: non c'è più differenza con 'adders' e Uscite di 'adders2'! –

risposta

34

L'obiettivo di:

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

è quello di creare una lista di add funzioni, il primo aggiunge 1 al suo ingresso, il secondo aggiunge 2, etc. valutazione pigro provoca R aspettare per creare davvero le vipere funziona fino a quando non inizi a chiamare veramente le funzioni. Il problema è che dopo aver creato la prima funzione di adder, il ciclo lapply viene aumentato dal ciclo lapply e termina con il valore 10. Quando si chiama la prima funzione di adder, la valutazione lazy ora crea la funzione, ottenendo il valore di x. Il problema è che l'originale x non è uguale a uno, ma al valore alla fine del ciclo lapply, cioè 10.

Pertanto, la valutazione pigra provoca tutte le funzioni sommatori aspettare fino a dopo il ciclo è stato completato lapply nel costruire davvero la funzione. Quindi creano la loro funzione con lo stesso valore, ad esempio 10. La soluzione suggerita da Hadley consiste nel forzare la valutazione di x direttamente, evitando la valutazione lenta e ottenendo le funzioni corrette con i valori corretti x.

+4

Ok, lasciatemi riformulare per vedere se ho capito bene. Quando chiamiamo 'lapply', R ordina di ricordare la struttura di tutte le 10 funzioni di adder, ma non valuta ancora x. Quando chiamiamo la prima funzione di adder, R dice, aha, vediamo di cosa si tratta, accetta x, che è già 10 in quel punto dalla chiamata lapply, e valuta la prima funzione di adder chiamata come 10 + y. Lo stesso vale per le restanti funzioni di aggiunta, che le rendono tutte identiche. Probabilmente messo in modo grossolano, ma è questa la logica? –

+0

Credo che sia un caso. –

+1

@ Maxim.K sì, è esattamente giusto. – hadley

52

Questo non è più vero a partire da R 3.2.0!

La riga corrispondente nella change log legge:

funzioni di ordine superiore come le funzioni applicare e ridurre() ora argomenti forza alle funzioni applicate per eliminare interazioni indesiderate tra valutazione pigra e cattura variabile in chiusure.

E infatti:

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

Ottime informazioni. Sconcertato anche me e ho pensato che avrebbe potuto modificare il lapply – AllYouCanEat86

Problemi correlati