LISP - Gestion des erreurs

Dans la terminologie commune LISP, les exceptions sont appelées conditions.

En fait, les conditions sont plus générales que les exceptions dans les langages de programmation traditionnels, car un condition représente toute occurrence, erreur ou non, qui pourrait affecter divers niveaux de pile d'appels de fonction.

Le mécanisme de gestion des conditions dans LISP gère de telles situations de telle manière que les conditions sont utilisées pour signaler un avertissement (par exemple en imprimant un avertissement) tandis que le code de niveau supérieur sur la pile d'appels peut continuer son travail.

Le système de gestion des conditions dans LISP comprend trois parties -

  • Signalisation d'une condition
  • Gérer la condition
  • Redémarrez le processus

Gérer une condition

Prenons un exemple de manipulation d'une condition résultant d'une condition de division par zéro, pour expliquer les concepts ici.

Vous devez suivre les étapes suivantes pour gérer une condition -

  • Define the Condition - "Une condition est un objet dont la classe indique la nature générale de la condition et dont les données d'instance contiennent des informations sur les détails des circonstances particulières qui conduisent à la signalisation de la condition".

    La macro define-condition est utilisée pour définir une condition, qui a la syntaxe suivante -

(define-condition condition-name (error)
   ((text :initarg :text :reader text))
)
  • Les nouveaux objets de condition sont créés avec la macro MAKE-CONDITION, qui initialise les emplacements de la nouvelle condition en fonction du :initargs argument.

Dans notre exemple, le code suivant définit la condition -

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
  • Writing the Handlers- un gestionnaire de condition est un code utilisé pour traiter la condition signalée sur celui-ci. Il est généralement écrit dans l'une des fonctions de niveau supérieur qui appellent la fonction d'erreur. Lorsqu'une condition est signalée, le mécanisme de signalisation recherche un gestionnaire approprié en fonction de la classe de la condition.

    Chaque gestionnaire se compose de -

    • Spécificateur de type, qui indique le type de condition qu'il peut gérer
    • Une fonction qui prend un seul argument, la condition

    Lorsqu'une condition est signalée, le mécanisme de signalisation trouve le gestionnaire établi le plus récemment qui est compatible avec le type de condition et appelle sa fonction.

    La macro handler-caseétablit un gestionnaire de conditions. La forme de base d'un cas de gestionnaire -

(handler-case expression error-clause*)

Où, chaque clause d'erreur est de la forme -

condition-type ([var]) code)
  • Restarting Phase

    C'est le code qui récupère réellement votre programme des erreurs, et les gestionnaires de conditions peuvent ensuite gérer une condition en appelant un redémarrage approprié. Le code de redémarrage est généralement placé dans les fonctions de niveau intermédiaire ou de bas niveau et les gestionnaires de conditions sont placés dans les niveaux supérieurs de l'application.

    le handler-bindmacro vous permet de fournir une fonction de redémarrage, et vous permet de continuer aux fonctions de niveau inférieur sans dérouler la pile d'appels de fonction. En d'autres termes, le flux de contrôle sera toujours dans la fonction de niveau inférieur.

    La forme de base de handler-bind est comme suit -

(handler-bind (binding*) form*)

Où chaque liaison est une liste des éléments suivants -

  • un type de condition
  • une fonction de gestionnaire d'un argument

le invoke-restart macro trouve et appelle la fonction de redémarrage liée la plus récemment avec le nom spécifié comme argument.

Vous pouvez avoir plusieurs redémarrages.

Exemple

Dans cet exemple, nous démontrons les concepts ci-dessus en écrivant une fonction nommée division-function, qui créera une condition d'erreur si l'argument diviseur est zéro. Nous avons trois fonctions anonymes qui offrent trois façons d'en sortir - en renvoyant une valeur 1, en envoyant un diviseur 2 et en recalculant, ou en renvoyant 1.

Créez un nouveau fichier de code source nommé main.lisp et tapez le code suivant dedans.

(define-condition on-division-by-zero (error)
   ((message :initarg :message :reader message))
)
   
(defun handle-infinity ()
   (restart-case
      (let ((result 0))
         (setf result (division-function 10 0))
         (format t "Value: ~a~%" result)
      )
      (just-continue () nil)
   )
)
     
(defun division-function (value1 value2)
   (restart-case
      (if (/= value2 0)
         (/ value1 value2)
         (error 'on-division-by-zero :message "denominator is zero")
      )

      (return-zero () 0)
      (return-value (r) r)
      (recalc-using (d) (division-function value1 d))
   )
)

(defun high-level-code ()
   (handler-bind
      (
         (on-division-by-zero
            #'(lambda (c)
               (format t "error signaled: ~a~%" (message c))
               (invoke-restart 'return-zero)
            )
         )
         (handle-infinity)
      )
   )
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'return-value 1)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'recalc-using 2)
         )
      )
   )
   (handle-infinity)
)

(handler-bind
   (
      (on-division-by-zero
         #'(lambda (c)
            (format t "error signaled: ~a~%" (message c))
            (invoke-restart 'just-continue)
         )
      )
   )
   (handle-infinity)
)

(format t "Done."))

Lorsque vous exécutez le code, il renvoie le résultat suivant -

error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.

Outre le «système de conditions», comme discuté ci-dessus, Common LISP fournit également diverses fonctions qui peuvent être appelées pour signaler une erreur. Le traitement d'une erreur, lorsqu'elle est signalée, dépend toutefois de la mise en œuvre.

Fonctions de signalisation d'erreur dans LISP

Le tableau suivant présente les fonctions couramment utilisées pour signaler les avertissements, les pauses, les erreurs non fatales et fatales.

Le programme utilisateur spécifie un message d'erreur (une chaîne). Les fonctions traitent ce message et peuvent / peuvent ne pas l'afficher à l'utilisateur.

Les messages d'erreur doivent être construits en appliquant le format fonction, ne doit pas contenir de caractère de nouvelle ligne au début ou à la fin, et n'a pas besoin d'indiquer une erreur, car le système LISP s'en chargera selon son style préféré.

N ° Sr. Fonction et description
1

error format-string & rest args

Cela signale une erreur fatale. Il est impossible de continuer à partir de ce genre d'erreur; ainsi l'erreur ne reviendra jamais à son appelant.

2

cerror continuer-format-string error-format-string & rest args

Il signale une erreur et entre dans le débogueur. Cependant, cela permet au programme de continuer à partir du débogueur après avoir résolu l'erreur.

3

warn format-string & rest args

il imprime un message d'erreur mais n'entre normalement pas dans le débogueur

4

break& optionnel format-string & rest args

Il imprime le message et va directement dans le débogueur, sans permettre aucune possibilité d'interception par des fonctions de gestion d'erreur programmées

Exemple

Dans cet exemple, la fonction factorielle calcule la factorielle d'un nombre; cependant, si l'argument est négatif, il déclenche une condition d'erreur.

Créez un nouveau fichier de code source nommé main.lisp et tapez le code suivant dedans.

(defun factorial (x)
   (cond ((or (not (typep x 'integer)) (minusp x))
      (error "~S is a negative number." x))
      ((zerop x) 1)
      (t (* x (factorial (- x 1))))
   )
)

(write(factorial 5))
(terpri)
(write(factorial -1))

Lorsque vous exécutez le code, il renvoie le résultat suivant -

120
*** - -1 is a negative number.