The right way to have two functions to access the same functional environment?

Based on the answer presented in 1088639 , I installed a couple of functions that both have access to the same subfunction environment. This example works, but I wanted to see if I missed a cleaner way to β€œconnect” both top-level functions to the internal environment.

(Background: I wanted to write a couple of additional functions that shared a variable, such as "count" in this example, and that met the requirements for the CRAN package that prevent functions from changing the global environment.)

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 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) } 

Output Example:

 > 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 
+6
source share
4 answers

You can get rid of factory functions and use environments more explicitly. A similar solution will also work

 .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) } 

The .env variable is created by immediately executing an anonymous function to get its environment. Then we extract the function from the environment itself to change its values.

+3
source

A cleaner method would be to use an object-oriented approach. There is already an answer using reference classes.

A typical object-oriented approach with classes would create a class and then create a singleton object, i.e. one object of this class. Of course, it's a little wasteful to create a class just to create one object from it, so here we give an example of proto. (Creating a function to enable count , and a function that does the real work, has a similar problem - you create a closing function only to run it once.) The proton model allows you to create an object directly bypassing the need to create a class to use it once. Here foobar is a proto-object with the count property and stats , statfoo and statbar . Note that we excluded stats to avoid duplicating its code in each of statfoo and statbar . (continued further)

 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) 

giving:

 > 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 

The second construct would be to have statfoo and statbar as independent functions and store count and stats in foobar (continued further)

 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) 

gives a similar conclusion in the previous example.

The third approach . Of course, the second variation can be easily implemented using local and a function that brings us closer to where you started. This does not use any packages, but does not create a function just to remove it:

 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) 
+5
source

You can use the reference class as follows:

 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) 

It is relatively clear that neither statfoo nor statbar is a pure function.

+4
source

Another easy way is to create an environment and assign it to both functions. Here I use simpler functions for illustrative purposes, but this can be easily expanded:

 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)) 

Then:

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

Both functions have the same environment.

+4
source

Source: https://habr.com/ru/post/987800/


All Articles