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 <maech...@stat.math.ethz.ch> wrote: >>>>>> Duncan Murdoch <murdoch.dun...@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@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. ______________________________________________ R-help@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.