Withindosim(), the line
sim <<- list()
would be replaced by
assign("simenv",new.env(),envir=.GlobalEnv)
This would create a new environment, pointed to bysimenvat the top
level. It would serve as a package in which to encapsulate our globals. We
would access them viaget()andassign(). For instance, the lines
if (is.null(sim$evnts)) {
sim$evnts <<- newevnt
inschedevnt()would become
if (is.null(get("evnts",envir=simenv))) {
assign("evnts",newevnt,envir=simenv)
Yes, this is cluttered too, but at least it is not complex like lists of lists
of lists. And it does protect against unwittingly writing to an unrelated vari-
able the user has at the top level. Using the superassignment operator still
yields the least cluttered code, but this compromise is worth considering.
As usual, there is no single style of programming that produces the best
solution in all applications. The globals approach is another option to con-
sider for your programming tool kit.
7.8.5 Closures.......................................................
Recall that an Rclosureconsists of a function’s arguments and body together
with itsenvironmentat the time of the call. The fact that the environment
is included is exploited in a type of programming that uses a feature also
known (in a slight overloading of terminology) as aclosure.
A closure consists of a function that sets up a local variable and then
creates another function that accesses that variable. This is a very abstract
description, so let’s go right to an example.^1
1 > counter
2 function () {
3 ctr <- 0
4 f <- function() {
5 ctr <<- ctr + 1
6 cat("this count currently has value",ctr,"\n")
7 }
(^1) Adapted from an example in “Top-level Task Callbacks in R,” by Duncan Temple Lang
(2001),http://developer.r-project.org/TaskHandlers.pdf.
174 Chapter 7