[R] Persistent state in a function?

Martin Maechler maechler at stat.math.ethz.ch
Mon Mar 21 10:41:35 CET 2016


>>>>> Duncan Murdoch <murdoch.duncan at gmail.com>
>>>>>     on Sat, 19 Mar 2016 17:57:56 -0400 writes:

    > On 19/03/2016 12:45 PM, Boris Steipe wrote:
    >> Dear all -
    >> 
    >> I need to have a function maintain a persistent lookup table of results for an expensive calculation, a named vector or hash. I know that I can just keep the table in the global environment. One problem with this approach is that the function should be able to delete/recalculate the table and I don't like side-effects in the global environment. This table really should be private. What I don't know is:
    >> -A- how can I keep the table in an environment that is private to the function but persistent for the session?
    >> -B- how can I store and reload such table?
    >> -C- most importantly: is that the right strategy to initialize and maintain state in a function in the first place?
    >> 
    >> 
    >> For illustration ...
    >> 
    >> -----------------------------------
    >> 
    >> myDist <- function(a, b) {
    >> # retrieve or calculate distances
    >> if (!exists("Vals")) {
    >> Vals <<- numeric() # the lookup table for distance values
    >> # here, created in the global env.
    >> }
    >> key <- sprintf("X%d.%d", a, b)
    >> thisDist <- Vals[key]
    >> if (is.na(thisDist)) {          # Hasn't been calculated yet ...
    >> cat("Calculating ... ")
    >> thisDist <- sqrt(a^2 + b^2) # calculate with some expensive function ...
    >> Vals[key] <<- thisDist      # store in global table
    >> }
    >> return(thisDist)
    >> }
    >> 
    >> 
    >> # run this
    >> set.seed(112358)
    >> 
    >> for (i in 1:10) {
    >> x <- sample(1:3, 2)
    >> print(sprintf("d(%d, %d) = %f", x[1], x[2], myDist(x[1], x[2])))
    >> }


    > Use local() to create a persistent environment for the function.  For 
    > example:

    > f <- local({
    > x <- NULL
    > function(y) {
    > cat("last x was ", x, "\n")
    > x <<- y
    > }
    > })

    > Then:

    >> f(3)
    > last x was
    >> f(4)
    > last x was  3
    >> f(12)
    > last x was  4

    > Duncan Murdoch

Yes, indeed.
Or use another function {than 'local()'} which returns a
function:  The functions  approxfun(), splinefun() and ecdf()
are "base R" functions which return functions "with a
non-trivial environment" as I use to say.

Note that this is *the* proper R way solving your problem.

The fact that this works as it works is called "lexical scoping"
and also the reason why (((regular, i.e., non-primitive)))
functions in R are called closures.
When R was created > 20 years ago, this has been the
distinguishing language feature of R (in comparison to S / S-plus).

Enjoy! - Martin



More information about the R-help mailing list