2013-12-25 3 views
19

Как я могу просто сказать 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 
     } 
    } 
) 

ответ

28

Я обычно ставлю на 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() 
) 
} 
+0

Должен установить попытку = 0 при запуске. – horaceT

5

Вот функция для создания пользовательского условия, чтобы ответить на

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") 
}) 
6

Я написал функцию быстрой, которая позволяет легко повторить работающее настраиваемое число раз, с настраиваемым ожиданием между попытками:

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.

Это альтернатива, которую я нашел до сих пор, что приводит к более четкому и понятному коду.

Надеюсь, это поможет.

5

Раствор без предварительного присвоения значений и использование 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

Смежные вопросы