Persistent state in a function?
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.