Использовать tryCatch для перехода к следующему значению цикла при ошибке? - PullRequest
54 голосов
/ 11 ноября 2011

Я прочитал несколько других SO вопросов о tryCatch и куззинах, а также документацию:

но я все еще не понимаю.

Я запускаю цикл и хочу перейти к next, если произойдет какая-либо из нескольких ошибок:

for (i in 1:39487) {

  # EXCEPTION HANDLING
  this.could.go.wrong <- tryCatch(
                           attemptsomething(),
                           error=function(e) next
                         )
  so.could.this <- tryCatch(
                     doesthisfail(),
                     error=function(e) next
                   )

  catch.all.errors <- function() { this.could.go.wrong; so.could.this; }
  catch.all.errors;


  #REAL WORK
  useful(i); fun(i); good(i);

  }  #end for

(кстати, документации по next, которую я могу найти)

Когда я запускаю это, R гудит:

Error in value[[3L]](cond) : no loop for break/next, jumping to top level

Какой основной момент я здесь упускаю? tryCatch явно находятся в цикле for, так почему же R не знает об этом?

Ответы [ 5 ]

75 голосов
/ 11 ноября 2011

Ключом к использованию tryCatch является понимание того, что он возвращает объект. Если внутри tryCatch произошла ошибка, этот объект будет наследоваться от класса error. Вы можете проверить наследование класса с помощью функции inherit.

x <- tryCatch(stop("Error"), error = function(e) e)
class(x)
"simpleError" "error"       "condition"  

Редактировать:

Что означает аргумент error = function(e) e? Это сбило меня с толку, и я не думаю, что это хорошо объяснено в документации. Что происходит, так это то, что этот аргумент перехватывает любые сообщения об ошибках, которые возникают в выражении, которое вы tryCatch ing. Если ошибка обнаружена, она возвращается как значение tryCatch. В справочной документации это описано как calling handler. Аргумент e внутри error=function(e) - это сообщение об ошибке, исходящее из вашего кода.


Я из старой школы процедурного программирования, где использование next было плохой вещью. Поэтому я бы переписал ваш код примерно так. (Обратите внимание, что я удалил оператор next внутри tryCatch.):

for (i in 1:39487) {
  #ERROR HANDLING
  possibleError <- tryCatch(
      thing(),
      error=function(e) e
  )

  if(!inherits(possibleError, "error")){
    #REAL WORK
    useful(i); fun(i); good(i);
  }

}  #end for

Функция next задокументирована внутри ? for`.

Если вы хотите использовать его вместо основной рабочей подпрограммы внутри if, ваш код должен выглядеть примерно так:

for (i in 1:39487) {
  #ERROR HANDLING
  possibleError <- tryCatch(
      thing(),
      error=function(e) e
  )

  if(inherits(possibleError, "error")) next

  #REAL WORK
  useful(i); fun(i); good(i);

}  #end for
5 голосов
/ 23 июля 2015
rm(list=ls())
for (i in -3:3) {
  #ERROR HANDLING
  possibleError <- tryCatch({
    print(paste("Start Loop ", i ,sep=""))
    if(i==0){
      stop()
    }
  }
    ,
    error=function(e) {
      e
      print(paste("Oops! --> Error in Loop ",i,sep = ""))
    }
  )

  if(inherits(possibleError, "error")) next

  print(paste("  End Loop ",i,sep = ""))

}
4 голосов
/ 29 марта 2012

Единственное действительно подробное объяснение, которое я видел, можно найти здесь: http://mazamascience.com/WorkingWithData/?p=912

Вот фрагмент кода из этого поста, показывающий, как работает tryCatch

#!/usr/bin/env Rscript
# tryCatch.r -- experiments with tryCatch

# Get any arguments
arguments <- commandArgs(trailingOnly=TRUE)
a <- arguments[1]

# Define a division function that can issue warnings and errors
myDivide <- function(d, a) {
  if (a == 'warning') {
    return_value <- 'myDivide warning result'
    warning("myDivide warning message")
  } else if (a == 'error') {
    return_value <- 'myDivide error result'
    stop("myDivide error message")
  } else {
    return_value = d / as.numeric(a)
  }
  return(return_value)
}

# Evalute the desired series of expressions inside of tryCatch
result <- tryCatch({

  b <- 2
  c <- b^2
  d <- c+2
  if (a == 'suppress-warnings') {
    e <- suppressWarnings(myDivide(d,a))
  } else {
    e <- myDivide(d,a) # 6/a
  }
  f <- e + 100

}, warning = function(war) {

  # warning handler picks up where error was generated
  print(paste("MY_WARNING:  ",war))
  b <- "changing 'b' inside the warning handler has no effect"
  e <- myDivide(d,0.1) # =60
  f <- e + 100
  return(f)

}, error = function(err) {

  # warning handler picks up where error was generated
  print(paste("MY_ERROR:  ",err))
  b <- "changing 'b' inside the error handler has no effect"
  e <- myDivide(d,0.01) # =600
  f <- e + 100
  return(f)

}, finally = {

  print(paste("a =",a))
  print(paste("b =",b))
  print(paste("c =",c))
  print(paste("d =",d))
  # NOTE:  Finally is evaluated in the context of of the inital
  # NOTE:  tryCatch block and 'e' will not exist if a warning
  # NOTE:  or error occurred.
  #print(paste("e =",e))

}) # END tryCatch

print(paste("result =",result))
3 голосов
/ 13 ноября 2011

Я упустил одну вещь, которую разрывает цикл for при запуске функции внутри цикла for в R , и это ясно:

  • next hasnне работает внутри функции.
  • Вам необходимо отправить какой-либо сигнал или флаг (например, Voldemort = TRUE) изнутри вашей функции (в моем случае tryCatch) наружу.
  • (это похоже на изменение глобальной общедоступной переменной внутри локальной закрытой функции)
  • Затем за пределами функции вы проверяете, был ли флаг отменен (выполняет Voldemort == TRUE).Если это так, вы вызываете break или next вне функции.
1 голос
/ 01 мая 2019

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

for (i in 1:10) {

  tryCatch(print(b), error = function(e) { print("hi"); skip_to_next <<- TRUE})

  if(skip_to_next) { next }     
}
...