2015-04-19 15 views
9

Se creo una funzione come segue:Puoi spiegare più chiaramente la valutazione pigra negli operatori di funzioni R?

what_is_love <- function(f) { 
    function(...) { 
    cat('f is', f, '\n') 
    } 
} 

e chiamarlo con lapply: funs <- lapply(c('love', 'cherry'), what_is_love)

ottengo uscita inaspettata:

> funs[[1]]() 
f is cherry 
> funs[[2]]() 
f is cherry 

Ma si noti che questo non è il caso quando si non utilizzare lapply:

> f1 <- what_is_love('love') 
> f2 <- what_is_love('cherry') 
> f1() 
f is love 
> f2() 
f is cherry 

Cosa dà?

so che funs <- lapply(c('love', 'cherry'), what_is_love) può essere scritto in maniera più completa:

params <- c('love', 'cherry') 
out <- vector('list', length(params)) 
for (i in seq_along(params)) { 
    out[[i]] <- what_is_love(params[[i]]) 
} 
out 

Ma quando navigo in, vedo che le due funzioni hanno il proprio ambiente:

Browse[1]> out[[1]] 
function(...) { 
    cat('f is', f, '\n') 
    } 
<environment: 0x109508478> 
Browse[1]> out[[2]] 
function(...) { 
    cat('f is', f, '\n') 
    } 
<environment: 0x1094ff750> 

Ma in ciascuno di tali ambienti, f è lo stesso ...

Browse[1]> environment(out[[1]])$f 
[1] "cherry" 
Browse[1]> environment(out[[2]])$f 
[1] "cherry" 

I kno w la risposta è "lazy evaluation", ma sto cercando un po 'più di profondità ... in che modo lo f viene ri-assegnato in entrambi gli ambienti? Da dove proviene f? In che modo la valutazione Razy funziona sotto il cofano in questo esempio?

-

EDIT: Sono consapevole di the other question sulla valutazione pigro e funzionali, ma si dice solo la risposta è "valutazione pigra" senza spiegare come la valutazione pigra funziona realmente. Sto cercando una maggiore profondità.

+0

possibile duplicato di [spiegare un pigro capriccio di valutazione] (http://stackoverflow.com/questions/16129902/explain-a-lazy-evaluation-quirk) – jangorecki

+1

lapply non è più comportamenti in questo modo in R-3.2. 0. –

risposta

11

Quando si esegue

what_is_love <- function(f) { 
    function(...) { 
    cat('f is', f, '\n') 
    } 
} 

la funzione interna crea un recinto per f, ma il problema è che fino a quando effettivamente uso una variabile passata a una funzione, rimane una "promessa" e non è effettivamente valutato. Se si desidera "acquisire" il valore corrente di f, è necessario forzare la valutazione della promessa; puoi usare la funzione force() per questo.

what_is_love <- function(f) { 
    force(f) 
    function(...) { 
    cat('f is', f, '\n') 
    } 
} 
funs <- lapply(c('love', 'cherry'), what_is_love) 

funs[[1]]() 
# f is love 
funs[[2]]() 
# f is cherry 

Senza force(), f rimane una promessa all'interno entrambe le funzioni nella vostra lista. Non viene valutato finché non si chiama la funzione e quando si chiama la funzione che viene promessa viene valutata sull'ultimo valore noto per f che è "cherry".

Come ha sottolineato @MartinMorgran, questo comportamento è stato modificato in R 3.2.0. Dalle release notes

funzioni di ordine superiore come le funzioni applicare e ridurre() ora forzare argomenti alle funzioni applicate per eliminare interazioni indesiderate tra valutazione pigra e cattura variabile nelle chiusure.Questo risolve PR # 16093.

+0

Fantastico, grazie per il dettaglio. Sareste in grado di spiegare un po 'di più in che modo R promette di lavorare sotto il cofano? – peterhurford

+0

Non riesci mai a lavorare con le promesse direttamente nel codice R, perché non appena le tocchi, scompaiono. Se vuoi sapere come sono implementati, dovrai leggere la [fonte R] (https://github.com/wch/r-source). C'è un po 'più di informazioni su di loro nella discussione di Hadley su [valutazione non standard] (http://adv-r.had.co.nz/Computing-on-language.html) – MrFlick

+0

Potresti anche essere interessato [pryr] (https://github.com/hadley/pryr) pacchetto che ha funzioni come 'promise_info()' per aiutare a estrarre informazioni sulle promesse. – MrFlick

Problemi correlati