
- LISP 教程
- LISP - 首頁
- LISP - 概述
- LISP - 環境
- LISP - 程式結構
- LISP - 基本語法
- LISP - 資料型別
- LISP - 宏
- LISP - 變數
- LISP - 常量
- LISP - 運算子
- LISP - 決策
- LISP - 迴圈
- LISP - 函式
- LISP - 謂詞
- LISP - 數字
- LISP - 字元
- LISP - 陣列
- LISP - 字串
- LISP - 序列
- LISP - 列表
- LISP - 符號
- LISP - 向量
- LISP - 集合
- LISP - 樹
- LISP - 雜湊表
- LISP - 輸入與輸出
- LISP - 檔案I/O
- LISP - 結構體
- LISP - 包
- LISP - 錯誤處理
- LISP - CLOS
- LISP 有用資源
- Lisp - 快速指南
- Lisp - 有用資源
- Lisp - 討論
LISP - 錯誤處理
在 Common LISP 術語中,異常被稱為條件。
事實上,條件比傳統程式語言中的異常更通用,因為條件表示任何可能影響函式呼叫棧各個級別的事件,無論是錯誤還是非錯誤。
LISP 中的條件處理機制以這樣一種方式處理此類情況:條件用於發出警告(例如列印警告),而呼叫棧上的上層程式碼可以繼續其工作。
LISP 中的條件處理系統包含三個部分:
- 發出條件
- 處理條件
- 重新啟動過程
處理條件
讓我們以一個處理因除以零引起的條件的示例來解釋這裡概念。
處理條件需要執行以下步驟:
定義條件 - “條件是一個物件,其類指示條件的總體性質,其例項資料攜帶有關導致發出條件的特定情況的詳細資訊”。
define-condition 宏用於定義條件,其語法如下:
(define-condition condition-name (error) ((text :initarg :text :reader text)) )
新的條件物件使用 MAKE-CONDITION 宏建立,它根據:initargs引數初始化新條件的槽。
在我們的示例中,以下程式碼定義了條件:
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)) )
編寫處理程式 - 條件處理程式是用於處理已發出的條件的程式碼。它通常編寫在調用出錯函式的高階函式之一中。當發出條件時,發出機制會根據條件的類搜尋合適的處理程式。
每個處理程式包含:
- 型別說明符,指示它可以處理的條件型別
- 一個函式,它接受一個引數:條件
當發出條件時,發出機制會找到與條件型別相容的最近建立的處理程式,並呼叫其函式。
宏handler-case建立條件處理程式。handler-case的基本形式:
(handler-case expression error-clause*)
其中,每個錯誤子句都具有以下形式:
condition-type ([var]) code)
重啟階段
這是實際從錯誤中恢復程式的程式碼,然後條件處理程式可以透過呼叫適當的重啟來處理條件。重啟程式碼通常放置在中級或低階函式中,而條件處理程式則放置在應用程式的上層。
handler-bind宏允許您提供重啟函式,並允許您在不展開函式呼叫棧的情況下繼續執行低階函式。換句話說,控制流仍然在低階函式中。
handler-bind的基本形式如下:
(handler-bind (binding*) form*)
其中每個繫結都是以下列表:
- 條件型別
- 一個帶有單個引數的處理程式函式
invoke-restart宏查詢並呼叫最近繫結的重啟函式,並使用指定的名稱作為引數。
您可以有多個重啟。
示例
在此示例中,我們透過編寫一個名為 division-function 的函式來演示上述概念,如果除數引數為零,該函式將建立錯誤條件。我們有三個匿名函式,它們提供了三種退出方法:返回一個值 1,傳送一個除數 2 並重新計算,或返回 1。
建立一個名為 main.lisp 的新原始碼檔案,並在其中鍵入以下程式碼。
(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."))
執行程式碼時,它將返回以下結果:
error signaled: denominator is zero Value: 1 error signaled: denominator is zero Value: 5 error signaled: denominator is zero Done.
除了上面討論的“條件系統”之外,Common LISP 還提供了各種可用於發出錯誤的函式。但是,發出錯誤後的錯誤處理方式取決於實現。
LISP 中的錯誤發出函式
下表提供了常用函式,用於發出警告、中斷、非致命和致命錯誤。
使用者程式指定錯誤訊息(字串)。這些函式處理此訊息,可能會或可能不會將其顯示給使用者。
錯誤訊息應透過應用format函式構建,開頭和結尾都不應包含換行符,並且不需要指示錯誤,因為 LISP 系統將根據其首選樣式處理這些錯誤。
序號 | 函式和描述 |
---|---|
1 |
error format-string &rest args 它發出致命錯誤。無法從這種錯誤中繼續;因此,error 永遠不會返回到其呼叫者。 |
2 |
cerror continue-format-string error-format-string &rest args 它發出錯誤並進入偵錯程式。但是,它允許程式在解決錯誤後從偵錯程式中繼續執行。 |
3 |
warn format-string &rest args 它列印錯誤訊息,但通常不會進入偵錯程式 |
4 |
break &optional format-string &rest args 它列印訊息並直接進入偵錯程式,不允許程式錯誤處理功能攔截。 |
示例
在此示例中,factorial 函式計算數字的階乘;但是,如果引數為負數,則會引發錯誤條件。
建立一個名為 main.lisp 的新原始碼檔案,並在其中鍵入以下程式碼。
(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))
執行程式碼時,它將返回以下結果:
120 *** - -1 is a negative number.