[R] Appropriate method for sharing data across functions

Duncan Murdoch
2012-04-09T17:21:00Z

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)