Reputation: 93
I'd like to make a basic profiling tool that collects time stamps and produces run times with a note. The only problem is I'm having trouble figuring out how to do this without using global variables. What's the "correct" way to implement the functionality I'm trying to achieve? If R already has this functionality built in, that's awesome, but what I'm really trying to figure out here is how to avoid using global variables and write more robust code.
timeStamps = c()
runTimes = list()
appendRunTimes <- function(note) {
if(length(timeStamps) < 1) {
timeStamps <<- Sys.time()
}
else {
timeStamps <<- c(timeStamps, Sys.time())
diff <- timeStamps[length(timeStamps) ] - timeStamps[length(timeStamps) - 1]
runTimes <<- c(runTimes, format(diff))
names(runTimes)[length(runTimes)] <<- note
}
}
appendRunTimes('start')
Sys.sleep(4)
appendRunTimes('test')
Upvotes: 6
Views: 1140
Reputation: 94192
Here's now to do this using ReferenceClasses:
RTmonitor = setRefClass("RTmonitor",
fields=list(
timeStamps="POSIXct",
runTimes = "list"
),
methods=list(
appendRunTimes=function(note){
if(length(timeStamps)==0){
timeStamps <<- Sys.time()
}else{
timeStamps <<- c(timeStamps, Sys.time())
diff <- timeStamps[length(timeStamps) ] - timeStamps[length(timeStamps) - 1]
runTimes <<- c(runTimes, format(diff))
names(runTimes)[length(runTimes)] <<- note
}
}
)
)
Now you've defined a class, instantiate an object and use it:
> r = RTmonitor$new()
> r$appendRunTimes("start")
> r$appendRunTimes("test")
> r
Reference class object of class "RTmonitor"
Field "timeStamps":
[1] "2013-01-05 14:52:25 GMT" "2013-01-05 14:52:31 GMT"
Field "runTimes":
$test
[1] "5.175815 secs"
Very similar to the closure approach, but more formalised. I had to define the timeStamps
field as POSIXct
, for example. You can also create multiple RTmonitor
objects this way and they work independently - you'd have to write a closure constructor that wrapped the closure to do that.
Upvotes: 4
Reputation: 72741
Here's your example rewritten using closures:
RTmonitor <- local({
timeStamps = c()
runTimes = list()
list(
appendRunTimes=function(note) {
if(length(timeStamps) < 1) {
timeStamps <<- Sys.time()
}
else {
timeStamps <<- c(timeStamps, Sys.time())
diff <- timeStamps[length(timeStamps) ] - timeStamps[length(timeStamps) - 1]
runTimes <<- c(runTimes, format(diff))
names(runTimes)[length(runTimes)] <<- note
}
},
viewRunTimes=function() {
return(list(timeStamps=timeStamps,runTimes=runTimes))
})
})
> RTmonitor$appendRunTimes("start")
> RTmonitor$appendRunTimes("test")
> RTmonitor$viewRunTimes()
$timeStamps
[1] "2013-01-04 18:39:12 EST" "2013-01-04 18:39:21 EST"
$runTimes
$runTimes$test
[1] "8.855587 secs"
Observe that the values are stored inside the closure, not in the global environment:
> timeStamps
Error: object 'timeStamps' not found
> runTimes
Error: object 'runTimes' not found
> RTmonitor$timeStamps
NULL
> RTmonitor$runTimes
NULL
More reading on closures and avoiding globals:
Upvotes: 8