[R] Appropriate method for sharing data across functions

Duncan Murdoch murdoch.duncan at gmail.com
Mon Apr 9 19:21:00 CEST 2012


On 05/04/2012 4:20 PM, John C Nash wrote:
> In trying to streamline various optimization functions, I would like to have a scratch pad
> of working data that is shared across a number of functions. These can be called from
> different levels within some wrapper functions for maximum likelihood and other such
> computations. I'm sure there are other applications that could benefit from this.
>
> Below are two approaches. One uses the<<- assignment to a structure I call OPCON. The
> other attempts to create an environment with this name, but fails. Though I have looked at
> a number of references, I have so far not found an adequate description of how to specify
> where the OPCON environment is located. (Both the green and blue books do not cover this
> topic, at least not under "environment" in the index.)
>
> Is there a recommended approach to this?


The one I would use is to create all of the functions that need access 
to the scratch pad as local functions within another.  For example,

makethem <- function() {
   MAXIMIZE <- TRUE
   PARSCALE <- rep(1, npar)
   KFN <- 0

   ... etc ...

   add1 <- function(){
     KFN <<- 1 + KFN
   }

   list(add1 = add1, ... other functions here...)
}

A single call to makethem() will return a list of functions which all 
have access to MAXIMIZE, PARSCALE, etc.  If you like, you can pull them 
out of the list to be called independently, e.g.

fns <- makethem()
add1 <- fns$add1

and they'll still have access.  (If this is a one-off need, you can use 
local( ) around the definitions, but I prefer the wrapper-function style.

> I realize I could use argument lists, but they
> get long and tedious with the number of items I may need to pass, though passing the OPCON
> structure in and out might be the proper way. An onAttach() approach was suggested by Paul
> Gilbert and tried, but it has so far not succeeded and, unfortunately, does not seem to be
> usable from source() i.e., cannot be interpreted but must be built first.
>
> JN
>
> Example using<<-
>
> rm(list=ls())
> optstart<-function(npar){ # create structure for optimization computations
>      # npar is number of parameters ?? test??
>      OPCON<<-list(MAXIMIZE=TRUE, PARSCALE=rep(1,npar), FNSCALE=1,
>          KFN=0, KGR=0, KHESS=0)
>      # may be other stuff
>      ls(OPCON)
> }

The code above creates OPCON as a global variable; that's dangerous, if 
you have multiple different optimizers potentially being used.
>
> add1<-function(){
>      OPCON$KFN<<-1+OPCON$KFN
>      test<-OPCON$KFN
> }
>
> OPCON<<-list(MAXIMIZE=TRUE, PARSCALE=rep(1,4), FNSCALE=1,
>          KFN=0, KGR=0, KHESS=0)
> ls(OPCON)
> print(add1())
> print(add1())
> print(ls.str())
>
> rm(OPCON) # Try to remove the scratchpad
> print(ls())
>
> tmp<-readline("Now try from within a function")
> setup<-optstart(4) # Need to sort out how to set this up appropriately
> cat("setup =")
> print(setup)
>
> print(add1())
> print(add1())
>
> rm(OPCON) # Try to remove the scratchpad
>
> ======================
> Example (failing) using new.env:
>
> rm(list=ls())
> optstart<-function(npar){ # create structure for optimization computations
>      # npar is number of parameters ?? test??
>      OPCON<-new.env(parent=globalenv())
>      OPCON<-list(MAXIMIZE=TRUE, PARSCALE=rep(1,npar), FNSCALE=1,
>          KFN=0, KGR=0, KHESS=0)

This failed because the second assignment wiped out the first:  you 
created a new environment, then threw it away when you assigned a list 
to OPCON.  The other problem with this approach is that you never saved 
OPCON anywhere, so when optstart() was done, it would disappear.

Hadley suggested using a reference class, and Bill suggested using an 
environment explicitly; those are both possible alternatives.  My 
version creates an environment but never names it; it's the environment 
of the add1() function and other functions defined within makethem().  
I'd say the more formal versions they suggested would be better if you 
had a longer (or open-ended)  list of possible functions you wanted to 
work with, or if you wanted to pass your OPCON to the user to manipulate.

Duncan Murdoch

>      # may be other stuff
>      ls(OPCON)
> }
>
> add1<-function(){
>      OPCON$KFN<-1+OPCON$KFN
>      test<-OPCON$KFN
> }
>
> OPCON<-new.env(parent=globalenv())
> OPCON<-list(MAXIMIZE=TRUE, PARSCALE=rep(1,4), FNSCALE=1,
>          KFN=0, KGR=0, KHESS=0)
> ls(OPCON)
> print(add1())
> print(add1())
> print(ls.str())
>
> rm(OPCON) # Try to remove the scratchpad
> print(ls())
>
> tmp<-readline("Now try from within a function")
> setup<-optstart(4) # Need to sort out how to set this up appropriately
> cat("setup =")
> print(setup)
>
> print(add1())
> print(add1())
>
> rm(OPCON) # Try to remove the scratchpad
>
> ______________________________________________
> R-help at r-project.org mailing list
> https://stat.ethz.ch/mailman/listinfo/r-help
> PLEASE do read the posting guide http://www.R-project.org/posting-guide.html
> and provide commented, minimal, self-contained, reproducible code.



More information about the R-help mailing list