100 votes

Utilisez tryCatch pour passer à la valeur suivante de la boucle en cas d'erreur ?

J'ai lu quelques autres questions SO sur tryCatch et cuzzins, ainsi que la documentation :

mais je ne comprends toujours pas.

Je lance une boucle et je veux passer à next si certains types d'erreurs surviennent :

for (i in 1:39487) {

  # GESTION DES EXCEPTIONS
  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;

  #TRAVAIL RÉEL
  utile(i); amusant(i); bon(i);

  }  #fin de la boucle

(au fait, je ne trouve aucune documentation sur next)

Lorsque j'exécute cela, R klaxonne :

Erreur dans value[[3L]](cond) : aucune boucle pour break/next, sautant au niveau supérieur

Quel point de base me manque-t-il ici ? Les tryCatch sont clairement dans la boucle for, alors pourquoi R ne le sait-il pas ?

104voto

Andrie Points 66979

La clé pour utiliser tryCatch est de réaliser qu'il renvoie un objet. S'il y avait une erreur à l'intérieur du tryCatch, alors cet objet héritera de la classe error. Vous pouvez tester l'héritage de classe avec la fonction inherit.

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

Éditer:

Quelle est la signification de l'argument error = function(e) e? Cela m'a stupéfié, et je ne pense pas que ce soit bien expliqué dans la documentation. Ce qui se passe, c'est que cet argument attrape tous les messages d'erreur qui proviennent de l'expression que vous essayez d'utiliser avec tryCatch. Si une erreur est attrapée, elle est renvoyée comme valeur de tryCatch. Dans la documentation d'aide, cela est décrit comme un gestionnaire d'appel. L'argument e à l'intérieur de error=function(e) est le message d'erreur provenant de votre code.


Je viens de la vieille école de la programmation procédurale où utiliser next était une mauvaise chose. Donc, je réécrirais votre code quelque chose comme ceci. (Notez que j'ai enlevé l'instruction next à l'intérieur du tryCatch.):

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

  if(!inherits(possibleError, "error")){
    #VRAI TRAVAIL
    utile(i); amusant(i); bien(i);
  }

}  #fin pour

La fonction next est documentée dans ?for`.

Si vous voulez l'utiliser au lieu d'avoir votre routine principale de travail à l'intérieur d'un if, votre code devrait ressembler à ceci:

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

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

  #VRAI TRAVAIL
  utile(i); amusant(i); bien(i);

}  #fin pour

97voto

user5783745 Points 1214

J'ai trouvé d'autres réponses très confuses. Voici une implémentation extrêmement simple pour quiconque souhaite simplement passer à l'itération de boucle suivante en cas d'erreur

for (i in 1:10) {

  skip_to_next <- FALSE

  # Notez que print(b) échoue car b n'existe pas

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

  if(skip_to_next) { next }     
}

7voto

ewittry Points 61
for (i in -3:3) {
  #GESTION DES ERREURS
  possibleError <- tryCatch({
    print(paste("Démarrage Boucle ", i ,sep=""))
    if(i==0){
      stop()
    }
  }
    ,
    error=function(e) {
      e
      print(paste("Oups! --> Erreur dans la Boucle ",i,sep = ""))
    }
  )

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

  print(paste("  Fin Boucle ",i,sep = ""))

}

5voto

isomorphismes Points 1902

Une chose qui me manquait, que rompre une boucle for lors de l'exécution d'une fonction à l'intérieur d'une boucle for en R clarifie, c'est ceci:

  • next ne fonctionne pas à l'intérieur d'une fonction.
  • Vous devez envoyer un signal ou un indicateur (par exemple, Voldemort = TRUE) de l'intérieur de votre fonction (dans mon cas tryCatch) vers l'extérieur.
  • (c'est comme modifier une variable globale, publique à l'intérieur d'une fonction locale, privée)
  • Ensuite, à l'extérieur de la fonction, vous vérifiez si le drapeau a été agité (est-ce que Voldemort == TRUE). Si c'est le cas, vous appelez break ou next à l'extérieur de la fonction.

5voto

mmann1123 Points 422

L'explication la plus détaillée que j'ai vue se trouve ici : http://mazamascience.com/WorkingWithData/?p=912

Voici un extrait de code de cet article de blog montrant comment tryCatch fonctionne

#!/usr/bin/env Rscript
# tryCatch.r -- expériences avec tryCatch

# Obtenir les arguments
arguments <- commandArgs(trailingOnly=TRUE)
a <- arguments[1]

# Définir une fonction de division pouvant émettre des avertissements et des erreurs
myDivide <- function(d, a) {
  if (a == 'warning') {
    return_value <- 'résultat d'avertissement de myDivide'
    warning("message d'avertissement de myDivide")
  } else if (a == 'error') {
    return_value <- 'résultat d'erreur de myDivide'
    stop("message d'erreur de myDivide")
  } else {
    return_value = d / as.numeric(a)
  }
  return(return_value)
}

# Évaluer la série d'expressions souhaitée à l'intérieur de 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) {

  # le gestionnaire d'avertissement reprend là où l'erreur a été générée
  print(paste("MON_AVERTISSEMENT:  ",war))
  b <- "changer 'b' à l'intérieur du gestionnaire d'avertissement n'a pas d'effet"
  e <- myDivide(d,0.1) # =60
  f <- e + 100
  return(f)

}, error = function(err) {

  # le gestionnaire d'avertissement reprend là où l'erreur a été générée
  print(paste("MON_ERREUR:  ",err))
  b <- "changer 'b' à l'intérieur du gestionnaire d'erreur n'a pas d'effet"
  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 est évalué dans le contexte du bloc initial de tryCatch et 'e' n'existera pas si un avertissement ou une erreur s'est produit.
  #print(paste("e =",e))

}) # FIN de tryCatch

print(paste("résultat =",result))

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X