Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Proper way to have two functions access a single function's environment?

Tags:

scope

r

Based on the answer provided in1088639, I set up a pair of functions which both access the same sub-function's environment. This example works, but I wanted to see if I'd missed some cleaner way to "connect" both top-level functions to the internal environment.

( Back story: I wanted to write a pair of complementary functions which shared a variable, e.g. "count" in this example, and meet CRAN package requirements which do not allow functions to modify 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'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)
}

Sample 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
like image 452
Carl Witthoft Avatar asked May 23 '15 18:05

Carl Witthoft


1 Answers

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. a single object of that class. Of course it is a bit wasteful to create a class only to create one object from it so here we provide a proto example. (Creating a function to enclose count and the function doing the real work has a similar problem -- you create an enclosing function only to run it once.) The proto model allows one to create an object directly bypassing the need to create a class only to use it once. Here foobar is the proto object with property count and methods stats, statfoo and statbar. Note that we factored out stats to avoid duplicating its code in each of statfoo and statbar. (continued further down)

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

A second design would be to have statfoo and statbar as independent functions and only keep count and stats in foobar (continued further down)

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)

giving similar output to the prior example.

Third approach Of course the second variation could easily be implemented by using local and a function getting us close to where you started. This does not use any packages but does not create a function only to throw it away:

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)
like image 82
G. Grothendieck Avatar answered Sep 22 '22 00:09

G. Grothendieck