Как повторить выражение об ошибке?
Как я могу просто сказать R несколько раз повторить инструкцию, если это ошибка? Например. Я надеялся сделать что-то вроде:
tryCatch(dbGetQuery(...), # Query database
error = function(e) {
if (is.locking.error(e)) # If database is momentarily locked
retry(times = 3) # retry dbGetQuery(...) 3 more times
else {
# Handle other errors
}
}
)
Ответы
Ответ 1
Я обычно помещаю блок try
в цикл,
и выйти из цикла, когда он больше не сбой или максимальное количество попыток достигнуто.
some_function_that_may_fail <- function() {
if( runif(1) < .5 ) stop()
return(1)
}
r <- NULL
attempt <- 1
while( is.null(r) && attempt <= 3 ) {
attempt <- attempt + 1
try(
r <- some_function_that_may_fail()
)
}
Ответ 2
Я написал быструю функцию, которая позволяет вам легко повторять операцию с настраиваемым количеством раз, с настраиваемым ожиданием между попытками:
library(futile.logger)
library(utils)
retry <- function(expr, isError=function(x) "try-error" %in% class(x), maxErrors=5, sleep=0) {
attempts = 0
retval = try(eval(expr))
while (isError(retval)) {
attempts = attempts + 1
if (attempts >= maxErrors) {
msg = sprintf("retry: too many retries [[%s]]", capture.output(str(retval)))
flog.fatal(msg)
stop(msg)
} else {
msg = sprintf("retry: error in attempt %i/%i [[%s]]", attempts, maxErrors,
capture.output(str(retval)))
flog.error(msg)
warning(msg)
}
if (sleep > 0) Sys.sleep(sleep)
retval = try(eval(expr))
}
return(retval)
}
Итак, вы можете просто написать val = retry(func_that_might_fail(param1, param2), maxErrors=10, sleep=2)
, чтобы повторить вызов этой функции с этими параметрами, отказаться от 10 ошибок и спящий 2 секунды между попытками.
Кроме того, вы можете переопределить значение того, что выглядит ошибка, передав другую функцию как параметр isError
, который по умолчанию поймает ошибку с сообщением stop
. Это полезно, если вызываемая функция делает что-то еще при ошибке, например, возвращение FALSE
или NULL
.
Это альтернатива, которую я нашел до сих пор, что приводит к более четкому и понятному коду.
Надеюсь, что это поможет.
Ответ 3
Решение без предварительно назначаемых значений и используя for
вместо while
:
some_function_that_may_fail <- function(i) {
if( runif(1) < .5 ) stop()
return(i)
}
for(i in 1:10){
try({
r <- some_function_that_may_fail(i)
break #break/exit the for-loop
}, silent = FALSE)
}
r
будет равно количеству попыток, которые были необходимы. Если вы не хотите, чтобы выходные данные ошибок устанавливались silent
на TRUE
Ответ 4
Здесь функция для создания пользовательского условия для ответа на
locked <- function(message="occurred", ...) {
cond <- simpleCondition(message, ...)
class(cond) <- c("locked", class(cond))
cond
}
и функция, реализованная для разрешения (бесконечного числа) перезапуска
f <- function() {
cnt <- 0L
repeat {
again <- FALSE
cnt <- cnt + 1L
withRestarts({
## do work here, and if needed...
signalCondition(locked())
}, retry=function() {
again <<- TRUE
})
if (!again) break
}
cnt
}
и использование withCallingHandlers
(чтобы сохранить контекст, в котором условие было сигнализировано активным, в отличие от tryCatch
) для обработки условия locked
withCallingHandlers({
n_tries <- 0L
f()
}, locked=function(e) {
n_tries <<- n_tries + 1L
if (n_retries < 3)
invokeRestart("retry")
})
Ответ 5
Мне нравится устанавливать свой объект как ошибку с самого начала, также иногда полезно добавить время сна, если у вас проблемы с подключением:
res <- simpleError("Fake error to start")
counter <- 1
max_tries <- 10
# Sys.sleep(2*counter)
while(inherits(res, "error") & counter < max_tries) {
res <- tryCatch({ your_fun(...) },
error = function(e) e)
counter <- counter + 1
}