[R] Persistent state in a function?

Bert Gunter bgunter.4567 at gmail.com
Mon Mar 21 16:19:33 CET 2016


Martin, All:

A very nice point! Perhaps the following may help to illustrate it.

g <- function(){
  x <- NULL
  function(y){cat("result is ",x," \n"); x <<- y}
}


> f <- g()

> rm(g) # g is deleted but its environment remains as the environment of f

> f(1)
result is

> f(3)
result is  1

> f(5)
result is  3


Best,
Bert





Bert Gunter

"The trouble with having an open mind is that people keep coming along
and sticking things into it."
-- Opus (aka Berkeley Breathed in his "Bloom County" comic strip )


On Mon, Mar 21, 2016 at 2:41 AM, Martin Maechler
<maechler at stat.math.ethz.ch> wrote:
>>>>>> 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
>
> ______________________________________________
> R-help at r-project.org mailing list -- To UNSUBSCRIBE and more, see
> 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