2015-05-23 10 views
6

In base alla risposta fornita in 1088639, ho impostato una coppia di funzioni che accedono entrambe allo stesso ambiente della sottofunzione. Questo esempio funziona, ma volevo vedere se mi fosse sfuggito un modo più semplice per "collegare" entrambe le funzioni di primo livello all'ambiente interno.Un modo corretto per consentire a due funzioni di accedere all'ambiente di una singola funzione?

(Indietro storia:. Volevo scrivere un paio di funzionalità complementari che condividono una variabile, ad esempio "contare" in questo esempio, e requisiti pacchetto incontrano CRAN che non consentono le funzioni di modificare l'ambiente globale)

static.f <- function() { 
    count <- 0 
    f <- function(x) { 
     count <<- count + 1 
     return(list(mean=mean(x), count=count)) 
    } 
    return(f) 
} 

# make sure not to delete this command, even tho' it's not 
# creating a function. 
f1 <- static.f() 

statfoo <- function(x){ 
    tmp<-f1(x) 
    tmp<- list(tmp,plus=2) 
    return(tmp) 
} 
statbar <- function(x){ 
    tmp<-f1(x) 
    tmp<- list(tmp,minus=3) 
    return(tmp) 
} 

Esempio di output:

> statfoo(5) 
[[1]] 
[[1]]$mean 
[1] 5 

[[1]]$count 
[1] 1 

$plus 
[1] 2 

Rgames> statfoo(5) 
[[1]] 
[[1]]$mean 
[1] 5 

[[1]]$count 
[1] 2 

$plus 
[1] 2 

> statbar(4) 
[[1]] 
[[1]]$mean 
[1] 4 

[[1]]$count 
[1] 3 

$minus 
[1] 3 

> statfoo(5) 
[[1]] 
[[1]]$mean 
[1] 5 

[[1]]$count 
[1] 4 

$plus 
[1] 2 
+0

Penso che tutte le risposte postate siano buone. Ho selezionato Flick perché non richiede un pacchetto aggiuntivo ('proto') e non richiede un costrutto' func $ part' o un comando di configurazione ('environment (f1) <- environment (f2) <- list2env (lista (count = 0)) ') come la risposta altrimenti eccellente di Brodie. –

risposta

3

Si potrebbe sbarazzarsi delle funzioni factory, e utilizzare in modo più esplicito gli ambienti. Una soluzione di questo tipo potrebbe funzionare così

.env<-(function() { 
    count <- 0 
    f <- function(x) { 
     count <<- count + 1 
     return(list(mean=mean(x), count=count)) 
    } 
    return(environment()) 
})() 



statfoo <- function(x){ 
    list(.env$f(x),plus=2) 
} 
statbar <- function(x){ 
    list(.env$f(x),minus=3) 
} 

La variabile è creata .env eseguendo immediatamente una funzione anonima per ottenere il suo ambiente. Quindi estraiamo la funzione dall'ambiente stesso per modificarne i valori.

4

È possibile utilizzare la classe di riferimento come questo:

foobar <- setRefClass(
    'foobar', 
    fields = list(count='numeric'), 
    methods = list(
     initialize=function() { 
      .self$initFields(count = 0L) 
     }, 
     statfoo = function(x) { 
      count <<- count + 1L 
      list(list(mean=mean(x), count=count), plus=2) 
     }, 
     statbar = function(x){ 
      count <<- count + 1L 
      list(list(mean=mean(x), count=count), minus=3) 
     } 
    ) 
)() 

foobar$statfoo(5) 
foobar$statbar(3) 

Si rende relativamente chiaro che né statfoostatbar è una pura funzione.

5

Un metodo più semplice sarebbe utilizzare un approccio orientato agli oggetti. C'è già una risposta usando le classi di riferimento.

Un tipico approccio orientato agli oggetti con classi creerebbe una classe e quindi creerà un oggetto singleton, ovvero un singolo oggetto di quella classe. Naturalmente è un po 'dispendioso creare una classe solo per crearne uno da qui, quindi qui forniamo un esempio di proto. (Creare una funzione per includere count e la funzione che esegue il lavoro reale ha un problema simile: si crea una funzione di inclusione solo per eseguirla una volta.) Il modello di proto consente di creare un oggetto ignorando direttamente la necessità di creare solo una classe usarlo una volta. Qui foobar è l'oggetto proto con proprietà count e metodi stats, statfoo e statbar. Si noti che abbiamo scomposto stats per evitare la duplicazione del codice in ciascuno di statfoo e statbar. (Continua più in basso)

library(proto) 

foobar <- proto(count = 0, 
      stats = function(., x) { 
       .$count <- .$count + 1 
       list(mean = mean(x), count = .$count) 
      }, 
      statfoo = function(., x) c(.$stats(x), plus = 2), 
      statbar = function(., x) c(.$stats(x), plus = 3) 
) 

foobar$statfoo(1:3) 
foobar$statbar(2:4) 

dando:

> foobar$statfoo(1:3) 
$mean 
[1] 2 

$count 
[1] 1 

$plus 
[1] 2 

> foobar$statbar(2:4) 
$mean 
[1] 3 

$count 
[1] 2 

$plus 
[1] 3 

Un secondo motivo sarebbe avere statfoo e statbar funzioni indipendenti e mantenere solo count e stats in foobar (continua più in basso)

library(proto) 

foobar <- proto(count = 0, 
      stats = function(., x) { 
       .$count <- .$count + 1 
       list(mean = mean(x), count = .$count) 
      } 
) 

statfoo <- function(x) c(foobar$stats(x), plus = 2) 
statbar <- function(x) c(foobar$stats(x), plus = 3) 

statfoo(1:3) 
statbar(2:4) 

dare simile fuori aggiungi all'esempio precedente.

Terzo approccio Naturalmente la seconda variante potrebbe essere facilmente implementato utilizzando local e una funzione a portarci vicino al punto di partenza.Questo non utilizza pacchetti ma non crea una funzione solo buttare via:

foobar <- local({ 
      count <- 0 
      function(x) { 
       count <<- count + 1 
       list(mean = mean(x), count = count) 
      } 
      }) 

statfoo <- function(x) c(foobar(x), plus = 2) 
statbar <- function(x) c(foobar(x), plus = 3) 

statfoo(1:3) 
statbar(2:4) 
4

Un'altra opzione semplice è tocreate un ambiente e assegnazione a entrambe le funzioni. Qui ho utilizzare le funzioni più semplici a scopo illustrativo, ma questo può essere facilmente estesa:

f1 <- function() {count <<- count + 1; return(paste("hello", count))} 
f2 <- function() {count <<- count + 1; return(paste("goodbye", count))} 

environment(f1) <- environment(f2) <- list2env(list(count=0)) 

Poi:

> f1() 
[1] "hello 1" 
> f2() 
[1] "goodbye 2" 
> f1() 
[1] "hello 3" 

Entrambe le funzioni hanno lo stesso ambiente.

Problemi correlati