首页 > 解决方案 > 如何在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 秒的五次运行的结果。

有人有想法吗?这在技术上是否可行?提前致谢!

标签: r

解决方案


我想你可以用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)
})

这样做的一个好处是,无论有限代码块的成功/中断如何,一旦完成,限制就会立即解除,因此无意中将其留在原位的风险较小。


推荐阅读