我想制作一个基本的分析工具,它收集时间戳并生成带有注释的运行时间。唯一的问题是我有麻烦弄清楚如何做到这一点,而不使用全局变量。实现我想要实现的功能的"正确"方式是什么?如果R已经内置了这个功能,那就太棒了,但我真正想弄清楚的是如何避免使用全局变量,并编写更健壮的代码。
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')
下面是使用闭包重写的示例:
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"
注意值存储在闭包中,而不是全局环境中:
> timeStamps
Error: object 'timeStamps' not found
> runTimes
Error: object 'runTimes' not found
> RTmonitor$timeStamps
NULL
> RTmonitor$runTimes
NULL
更多关于闭包和避免全局变量的阅读:
- 闭包作为数据合并习语的解决方案
- local()与R中的其他闭包方法有何不同?
- 为什么使用' <<- '会让人皱眉,我该如何避免使用它?
- R和Stata中全局变量的危险示例
现在使用referencecclasses:
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
}
}
)
)
现在你已经定义了一个类,实例化一个对象并使用它:
> 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"
与闭包方法非常相似,但更加形式化。例如,我必须定义timeStamps
域为POSIXct
。你也可以用这种方式创建多个RTmonitor
对象,它们独立工作——你必须编写一个闭包构造函数来包装闭包。