LISP - 錯誤處理
麵向對象的錯誤處理- LISP條件係統
在Common Lisp的術語中,異常被稱為條件。
事實上,條件比在傳統編程語言的異常更為普遍,因為一個條件表示任何事件,錯誤與否,這可能會影響各級函數調用堆棧。
在LISP狀態處理機製,處理的條件是用來警告信號(例如通過打印一個警告),而在調用堆棧的上層代碼可以繼續工作,這樣的情況下以這樣一種方式。
條件處理係統中LISP有三個部分:
-
信號的條件
-
處理條件
-
重啟進程
處理一個條件
讓我們處理由除零所產生的條件的例子,在這裡解釋這些概念。
需要處理的條件如下步驟:
-
定義條件 - “條件是一個對象,它的類表示條件的一般性質,其實例數據進行有關的特殊情況,導致被示意條件的細節信息”。
定義條件的宏用於定義一個條件,它具有以下語法:
(define-condition condition-name (error) ((text :initarg :text :reader text)))
:initargs 參數,新的條件對象與MAKE-CONDITION 宏,它初始化的基礎上,新的條件下的插槽中創建的。
在我們的例子中,下麵的代碼定義的條件:
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)))
-
編寫處理程序 - 條件處理程序是用於處理信號的條件在其上的代碼。它一般寫在調用該函數出問題的上級功能之一。當條件信號發生時,該信號轉導機製中搜索基於所述條件的類合適的處理器。
每個處理程序包括:
-
類型說明符,它指示條件,它可以處理的類型
-
一個函數,它接受一個參數條件
當條件獲得信號,該信號機製發現最近建立的處理程序與條件類型兼容,並調用它的函數。
宏處理程序的情況建立了一個條件處理程序。一個處理程序的 handler-case 形式:
(handler-case expression error-clause*)
那麼,每個error從句的形式為:
condition-type ([var]) code)
-
-
重新啟動階段
這是真正從錯誤的代碼中恢複程序,條件處理程序可以通過調用一個適當的重啟處理的條件。重啟代碼一般是放置在中層或底層函數和條件處理程序被放置到應用程序的上層。
handler-bind宏允許提供一個重啟功能,並允許繼續在較低級的功能,無需解除函數的調用堆棧。換句話說,控製流將仍然處於較低水平的功能。
handler-bind的基本形式如下:
(handler-bind (binding*) form*)
其中每個綁定如以下列表:
-
條件類型
-
一個參數的處理函數
invoke-restart宏查找並調用具有指定名稱作為參數最近綁定重啟功能。
可以有多個重新啟動。
-
示例
在這個例子中,我們演示了上述概念通過寫一個名為劃分功能函數,則會創建錯誤條件,如果除數參數為零。我們有三個匿名的功能,提供三種方式來出它 - 通過返回一個值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.
除了“係統狀態”,如上文所討論,普通的LISP還提供了各種功能,其可被稱為信令錯誤。當信號實現相關處理錯誤。
LISP的錯誤信號功能
下表提供了常用功能的信令警告,休息,非致命和致命的錯誤。
用戶程序指定一個錯誤信息(字符串)。該函數處理這個消息,並且可能/可能不會顯示給用戶。
錯誤信息應該通過應用的格式化功能進行構造,不應該在開頭或結尾包含一個換行符,也無需指明錯誤,如LISP係統將根據其喜好的樣式利用這些服務。
SL No. | 函數和說明 |
---|---|
1 |
error format-string &rest args 它標誌著一個致命的錯誤。這是不可能從這種錯誤的繼續;這樣的錯誤將永遠不會返回到其調用者。 |
2 |
cerror continue-format-string error-format-string &rest args 它發出錯誤信號,並進入調試器。但是,它允許程序從調試器解決錯誤之後繼續。 |
3 |
warn format-string &rest args 它打印一條錯誤消息,但一般不會進入調試 |
4 |
break &optional format-string &rest args 它打印的消息,並直接進入調試器,而不允許攔截由編程錯誤處理設施的任何可能性 |
示例
在這個例子中,階乘函數計算一個數階乘;但是,如果參數為負,它拋出一個錯誤條件。
創建一個名為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.