LISP - Fehlerbehandlung
In der allgemeinen LISP-Terminologie werden Ausnahmen als Bedingungen bezeichnet.
In der Tat sind die Bedingungen allgemeiner als Ausnahmen in traditionellen Programmiersprachen, weil a condition stellt jedes Auftreten, jeden Fehler oder nicht dar, das sich auf verschiedene Ebenen des Funktionsaufrufstapels auswirken kann.
Der Bedingungsbehandlungsmechanismus in LISP behandelt solche Situationen so, dass Bedingungen verwendet werden, um eine Warnung zu signalisieren (z. B. durch Drucken einer Warnung), während der Code der oberen Ebene auf dem Aufrufstapel seine Arbeit fortsetzen kann.
Das Zustandsbehandlungssystem in LISP besteht aus drei Teilen:
- Einen Zustand signalisieren
- Umgang mit der Bedingung
- Starten Sie den Prozess neu
Umgang mit einer Bedingung
Nehmen wir ein Beispiel für die Behandlung einer Bedingung, die sich aus der Bedingung Division durch Null ergibt, um die Konzepte hier zu erläutern.
Sie müssen die folgenden Schritte ausführen, um eine Bedingung zu behandeln:
Define the Condition - "Eine Bedingung ist ein Objekt, dessen Klasse die allgemeine Natur der Bedingung angibt und dessen Instanzdaten Informationen über die Details der besonderen Umstände enthalten, die zur Signalisierung der Bedingung führen."
Das Definitionsbedingungsmakro wird zum Definieren einer Bedingung verwendet, die die folgende Syntax hat:
(define-condition condition-name (error)
((text :initarg :text :reader text))
)
Neue Bedingungsobjekte werden mit dem Makro MAKE-CONDITION erstellt, das die Slots der neuen Bedingung basierend auf dem initialisiert :initargs Streit.
In unserem Beispiel definiert der folgende Code die Bedingung -
(define-condition on-division-by-zero (error)
((message :initarg :message :reader message))
)
Writing the Handlers- Ein Bedingungshandler ist ein Code, der zur Behandlung der darauf signalisierten Bedingung verwendet wird. Es wird im Allgemeinen in einer der übergeordneten Funktionen geschrieben, die die Fehlerfunktion aufrufen. Wenn eine Bedingung signalisiert wird, sucht der Signalisierungsmechanismus basierend auf der Klasse der Bedingung nach einem geeigneten Handler.
Jeder Handler besteht aus -
- Typspezifizierer, der den Typ der Bedingung angibt, mit der er umgehen kann
- Eine Funktion, die ein einzelnes Argument akzeptiert, die Bedingung
Wenn eine Bedingung signalisiert wird, findet der Signalisierungsmechanismus den zuletzt eingerichteten Handler, der mit dem Bedingungstyp kompatibel ist, und ruft seine Funktion auf.
Das Makro handler-caserichtet einen Bedingungshandler ein. Die Grundform eines Handler-Falls -
(handler-case expression error-clause*)
Wobei jede Fehlerklausel die Form hat -
condition-type ([var]) code)
Restarting Phase
Dies ist der Code, der Ihr Programm tatsächlich von Fehlern wiederherstellt, und Bedingungshandler können dann eine Bedingung behandeln, indem sie einen geeigneten Neustart aufrufen. Der Neustartcode wird im Allgemeinen in Funktionen der mittleren oder unteren Ebene platziert, und die Bedingungshandler werden in den oberen Ebenen der Anwendung platziert.
Das handler-bindMit dem Makro können Sie eine Neustartfunktion bereitstellen und mit den Funktionen der unteren Ebene fortfahren, ohne den Funktionsaufrufstapel abzuwickeln. Mit anderen Worten, der Steuerungsfluss befindet sich weiterhin in der Funktion der unteren Ebene.
Die Grundform von handler-bind ist wie folgt -
(handler-bind (binding*) form*)
Wobei jede Bindung eine Liste der folgenden ist:
- eine Konditionsart
- eine Handlerfunktion eines Arguments
Das invoke-restart Das Makro findet und ruft die zuletzt gebundene Neustartfunktion mit dem angegebenen Namen als Argument auf.
Sie können mehrere Neustarts durchführen.
Beispiel
In diesem Beispiel demonstrieren wir die obigen Konzepte, indem wir eine Funktion namens Division-Funktion schreiben, die eine Fehlerbedingung erzeugt, wenn das Divisor-Argument Null ist. Wir haben drei anonyme Funktionen, die drei Möglichkeiten bieten, um daraus herauszukommen - indem Sie einen Wert 1 zurückgeben, einen Divisor 2 senden und neu berechnen oder 1 zurückgeben.
Erstellen Sie eine neue Quellcodedatei mit dem Namen main.lisp und geben Sie den folgenden Code ein.
(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."))
Wenn Sie den Code ausführen, wird das folgende Ergebnis zurückgegeben:
error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.
Neben dem oben diskutierten "Bedingungssystem" bietet Common LISP auch verschiedene Funktionen, die zum Signalisieren eines Fehlers aufgerufen werden können. Die Behandlung eines Fehlers, wenn er signalisiert wird, ist jedoch implementierungsabhängig.
Fehlersignalisierungsfunktionen in LISP
Die folgende Tabelle enthält häufig verwendete Funktionen, die Warnungen, Unterbrechungen, nicht schwerwiegende und schwerwiegende Fehler signalisieren.
Das Anwenderprogramm gibt eine Fehlermeldung (eine Zeichenfolge) an. Die Funktionen verarbeiten diese Nachricht und zeigen sie dem Benutzer möglicherweise nicht an.
Die Fehlermeldungen sollten durch Anwenden von erstellt werden format Funktion, sollte weder am Anfang noch am Ende ein Zeilenumbruchzeichen enthalten und muss keinen Fehler anzeigen, da das LISP-System diese gemäß seinem bevorzugten Stil erledigt.
Sr.Nr. | Funktion und Beschreibung |
---|---|
1 | error Format-String & Rest Argumente Es signalisiert einen schwerwiegenden Fehler. Es ist unmöglich, von dieser Art von Fehler fortzufahren; Daher wird der Fehler niemals zu seinem Anrufer zurückkehren. |
2 | cerror continue-format-string error-format-string & rest args Es signalisiert einen Fehler und tritt in den Debugger ein. Es ermöglicht jedoch, das Programm vom Debugger aus fortzusetzen, nachdem der Fehler behoben wurde. |
3 | warn Format-String & Rest Argumente Es gibt eine Fehlermeldung aus, geht aber normalerweise nicht in den Debugger |
4 | break& optional format-string & rest args Es druckt die Nachricht und geht direkt in den Debugger, ohne dass programmierte Fehlerbehandlungsfunktionen abfangen können |
Beispiel
In diesem Beispiel berechnet die Fakultätsfunktion die Fakultät einer Zahl. Wenn das Argument jedoch negativ ist, wird eine Fehlerbedingung ausgelöst.
Erstellen Sie eine neue Quellcodedatei mit dem Namen main.lisp und geben Sie den folgenden Code ein.
(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))
Wenn Sie den Code ausführen, wird das folgende Ergebnis zurückgegeben:
120
*** - -1 is a negative number.