r - 如何在R中的一段时间后中断循环内的函数?
问题描述
我通过 R 中的 for 循环多次运行算法。我的循环非常基本,看起来像这样。
iter <- 5 #number of iterations
result <- list()
for (i in 1:iter) {
fit <- algorithm() #this is an example function that starts the algorithm
result[[i]] <- print(fit)
}
问题是每次运行的运行时间差异很大。有的跑只需要10分钟,有的需要一个多小时。但是,我知道较长的运行时间是由于算法由于初始值而存在问题,并且这些运行的结果无论如何都是错误的。
所以,我现在正在寻找一种解决方案,(1)在例如 1000 秒后中断函数(即上面示例中的算法()),(2)继续 for 循环,(3)为每个中断添加额外的迭代. 所以,最后,我想要运行时间少于 1000 秒的五次运行的结果。
有人有想法吗?这在技术上是否可行?提前致谢!
解决方案
我想你可以用setTimeLimit
这个。
快速演示:
setTimeLimit(elapsed = 2)
Sys.sleep(999)
# Error in Sys.sleep(999) : reached elapsed time limit
setTimeLimit(elapsed = Inf)
(重要的是要注意,当您不再希望中断时,您应该返回时间限制设置。)
我的“复杂算法”会随机休眠。那些随机长度是
set.seed(42)
sleeps <- sample(10, size=5)
sleeps
# [1] 1 5 10 8 2
我将设置一个 6 秒的任意限制,超过该限制睡眠将被中断,我们将不会得到任何返回值。这应该中断第三和第四个元素。
iter <- 5
result <- list()
for (i in seq_len(iter)) {
result[[i]] <- tryCatch({
setTimeLimit(elapsed = 6)
Sys.sleep(sleeps[[i]])
setTimeLimit(elapsed = Inf)
c(iter = i, slp = sleeps[[i]])
}, error = function(e) NULL)
}
result
# [[1]]
# iter slp
# 1 1
# [[2]]
# iter slp
# 2 5
# [[3]]
# NULL
# [[4]]
# NULL
# [[5]]
# iter slp
# 5 2
如果您有不同的“睡眠”并且最终得到的对象比您需要的要短,只需附加它:
result <- c(result, vector("list", 5 - length(result)))
对于几件事,我会稍微增强一下:
lapply
以这种方式for
填写时,我更喜欢循环;result
和- 由于复杂的算法可能由于其他原因而失败,如果我的睡眠提前失败,那么时间限制将不会被重置,所以我将使用
on.exit
,它确保在其外壳退出时调用一个函数,无论是否由于错误。
result <- lapply(seq_len(iter), function(i) {
setTimeLimit(elapsed = 6)
on.exit(setTimeLimit(elapsed = Inf), add = TRUE)
tryCatch({
Sys.sleep(sleeps[i])
c(iter = i, slp = sleeps[i])
}, error = function(e) NULL)
})
result
# [[1]]
# iter slp
# 1 1
# [[2]]
# iter slp
# 2 5
# [[3]]
# NULL
# [[4]]
# NULL
# [[5]]
# iter slp
# 5 2
在这种情况下,result
长度为 5,因为lapply
每次迭代都会返回一些东西。(lapply
对于 R 来说,使用是惯用的,它的效率通常在apply
和类似map
的方法中,不像其他语言,真正的速度是通过文字for
循环实现的。)
(顺便说一句:我也可以使用而不是on.exit
逻辑tryCatch(..., finally=setTimeLimit(elapsed=Inf))
。)
on.exit
逻辑的替代方法是在执行块内setTimeLimit(.., transient=TRUE)
使用以进行限制。这将使这段代码
result <- lapply(seq_len(iter), function(i) {
tryCatch({
setTimeLimit(elapsed = 6, transient = TRUE)
Sys.sleep(sleeps[i])
c(iter = i, slp = sleeps[i])
},
error = function(e) NULL)
})
这样做的一个好处是,无论有限代码块的成功/中断如何,一旦完成,限制就会立即解除,因此无意中将其留在原位的风险较小。