2011-10-26 8 views
16

Ieri ho imparato da Bill Venables how locale() può contribuire a creare le funzioni e le variabili statiche, per esempio,In che modo local() differisce dagli altri approcci alla chiusura in R?

example <- local({ 
    hidden.x <- "You can't see me!" 
    hidden.fn <- function(){ 
    cat("\"hidden.fn()\"") 
    } 
    function(){ 
    cat("You can see and call example()\n") 
    cat("but you can't see hidden.x\n") 
    cat("and you can't call ") 
    hidden.fn() 
    cat("\n") 
    } 
}) 

che si comporta come segue dal prompt dei comandi:

> ls() 
[1] "example" 
> example() 
You can see and call example() 
but you can't see hidden.x 
and you can't call "hidden.fn()" 
> hidden.x     
Error: object 'hidden.x' not found 
> hidden.fn() 
Error: could not find function "hidden.fn" 

Ho visto questo tipo di cosa discussa in Static Variables in R dove è stato utilizzato un approccio diverso.

Quali sono i pro e i contro di questi due metodi?

risposta

12

incapsulamento

Il vantaggio di questo stile di programmazione è che gli oggetti nascosti non saranno probabilmente sovrascritti da qualsiasi altra cosa in modo da poter essere più sicuri che essi contengono quello che pensi. Non saranno usati per errore poiché non possono essere facilmente accessibili. Nel post collegato alla domanda è presente una variabile globale, count, a cui è possibile accedere e sovrascrivere da qualsiasi posizione, quindi se eseguiamo il debug del codice e guardiamo a count e vediamo che è cambiato, non possiamo davvero sapere quale parte del codice ha cambiato. Al contrario, nel codice di esempio della domanda abbiamo la certezza che nessun'altra parte del codice è coinvolta.

Nota che in realtà in grado di accedere alla funzione nascosta anche se la sua non è così facile:

# run hidden.fn 
environment(example)$hidden.fn() 

Object Oriented Programming

noti inoltre che questo è molto vicino alla programmazione orientata agli oggetti in cui example e hidden.fn sono metodi e hidden.x è una proprietà. Potremmo fare in questo modo per rendere più esplicito:

library(proto) 
p <- proto(x = "x", 
    fn = function(.) cat(' "fn()"\n '), 
    example = function(.) .$fn() 
) 
p$example() # prints "fn()" 

proto non nasconde x e fn ma non è così facile accedervi per errore dal momento che è necessario utilizzare p$x e p$fn() per accedervi, che non è poi così diverso da poter scrivere e <- environment(example); e$hidden.fn()

EDIT:

l'approccio ad oggetti fa aggiungere la possibilità di eredità, es si potrebbe definire un figlio di p che agisce come p eccetto per il fatto che ha la precedenza su fn.

ch <- p$proto(fn = function(.) cat("Hello from ch\n")) # child 
ch$example() # prints: Hello from ch 
6

local() può implementare un pattern Singleton - per esempio, il pacchetto snow utilizza per monitorare la singola istanza RMPI che l'utente potrebbe creare.

getMPIcluster <- NULL 
setMPIcluster <- NULL 
local({ 
    cl <- NULL 
    getMPIcluster <<- function() cl 
    setMPIcluster <<- function(new) cl <<- new 
}) 

local() potrebbe anche essere utilizzato per gestire la memoria in uno script, ad esempio, allocare grandi oggetti intermedi necessari per creare un oggetto finale nell'ultima riga della clausola. I grandi oggetti intermedi sono disponibili per la garbage collection quando restituisce local.

L'utilizzo di una funzione per creare una chiusura è un modello predefinito: l'esempio bank account nella documentazione Introduzione a R, in cui ogni volta che viene richiamato open.account, viene creato un nuovo account.

Come @otsaw menzioni, Memoizzazione potrebbe essere implementato usando locale, ad esempio, ai siti web di cache in un cingolato

library(XML) 
crawler <- local({ 
    seen <- new.env(parent=emptyenv()) 
    .do_crawl <- function(url, base, pattern) { 
     if (!exists(url, seen)) { 
      message(url) 
      xml <- htmlTreeParse(url, useInternal=TRUE) 
      hrefs <- unlist(getNodeSet(xml, "//a/@href")) 
      urls <- 
       sprintf("%s%s", base, grep(pattern, hrefs, value=TRUE)) 
      seen[[url]] <- length(urls) 
      for (url in urls) 
       .do_crawl(url, base, pattern) 
     } 
    } 
    .do_report <- function(url) { 
     urls <- as.list(seen) 
     data.frame(Url=names(urls), Links=unlist(unname(urls)), 
        stringsAsFactors=FALSE) 
    } 
    list(crawl=function(base, pattern="^/.*html$") { 
     .do_crawl(base, base, pattern) 
    }, report=.do_report) 
}) 

crawler$crawl(favorite_url) 
dim(crawler$report()) 

(il solito esempio di Memoizzazione, numeri di Fibonacci, non è soddisfacente - la gamma di i numeri che non superano la rappresentazione numerica di R sono piccoli, quindi probabilmente si utilizzerà una tabella di ricerca di valori precalcolati in modo efficiente). Interessante come il crawler qui è un singleton; avrebbe potuto facilmente seguire uno schema di fabbrica, quindi un crawler per URL base.

+1

Un altro modello che 'local' rende conveniente è la memoizzazione. C'è un esempio da qualche parte in The R Inferno. – otsaw

+0

Il tuo pensiero sul crawler che è un singleton è interessante perché un'alternativa al local è di valutare immediatamente una funzione anonima senza argomenti - un pattern factory potrebbe utilizzare una chiusura sull'URL di base. – hadley