在Common Lisp的術(shù)語(yǔ)中,異常被稱為條件。
事實(shí)上,條件比在傳統(tǒng)編程語(yǔ)言的異常更為普遍,因?yàn)橐粋€(gè)條件表示任何事件,錯(cuò)誤與否,這可能會(huì)影響各級(jí)函數(shù)調(diào)用堆棧。
在LISP狀態(tài)處理機(jī)制,處理的條件是用來(lái)警告信號(hào)(例如通過(guò)打印一個(gè)警告),而在調(diào)用堆棧的上層代碼可以繼續(xù)工作,這樣的情況下以這樣一種方式。
條件處理系統(tǒng)中LISP有三個(gè)部分:
信號(hào)的條件
處理?xiàng)l件
重啟進(jìn)程
讓我們處理由除零所產(chǎn)生的條件的例子,在這里解釋這些概念。
需要處理的條件如下步驟:
定義條件 - “條件是一個(gè)對(duì)象,它的類表示條件的一般性質(zhì),其實(shí)例數(shù)據(jù)進(jìn)行有關(guān)的特殊情況,導(dǎo)致被示意條件的細(xì)節(jié)信息”。
定義條件的宏用于定義一個(gè)條件,它具有以下語(yǔ)法:
(define-condition condition-name (error) ((text :initarg :text :reader text)))
:initargs 參數(shù),新的條件對(duì)象與MAKE-CONDITION 宏,它初始化的基礎(chǔ)上,新的條件下的插槽中創(chuàng)建的。
在我們的例子中,下面的代碼定義的條件:
(define-condition on-division-by-zero (error) ((message :initarg :message :reader message)))
編寫(xiě)處理程序 - 條件處理程序是用于處理信號(hào)的條件在其上的代碼。它一般寫(xiě)在調(diào)用該函數(shù)出問(wèn)題的上級(jí)功能之一。當(dāng)條件信號(hào)發(fā)生時(shí),該信號(hào)轉(zhuǎn)導(dǎo)機(jī)制中搜索基于所述條件的類合適的處理器。
每個(gè)處理程序包括:
類型說(shuō)明符,它指示條件,它可以處理的類型
一個(gè)函數(shù),它接受一個(gè)參數(shù)條件
當(dāng)條件獲得信號(hào),該信號(hào)機(jī)制發(fā)現(xiàn)最近建立的處理程序與條件類型兼容,并調(diào)用它的函數(shù)。
宏處理程序的情況建立了一個(gè)條件處理程序。一個(gè)處理程序的 handler-case 形式:
(handler-case expression error-clause*)
那么,每個(gè)error從句的形式為:
condition-type ([var]) code)
重新啟動(dòng)階段
這是真正從錯(cuò)誤的代碼中恢復(fù)程序,條件處理程序可以通過(guò)調(diào)用一個(gè)適當(dāng)?shù)闹貑⑻幚淼臈l件。重啟代碼一般是放置在中層或底層函數(shù)和條件處理程序被放置到應(yīng)用程序的上層。
handler-bind宏允許提供一個(gè)重啟功能,并允許繼續(xù)在較低級(jí)的功能,無(wú)需解除函數(shù)的調(diào)用堆棧。換句話說(shuō),控制流將仍然處于較低水平的功能。
handler-bind的基本形式如下:
(handler-bind (binding*) form*)
其中每個(gè)綁定如以下列表:
條件類型
一個(gè)參數(shù)的處理函數(shù)
invoke-restart宏查找并調(diào)用具有指定名稱作為參數(shù)最近綁定重啟功能。
可以有多個(gè)重新啟動(dòng)。
示例
在這個(gè)例子中,我們演示了上述概念通過(guò)寫(xiě)一個(gè)名為劃分功能函數(shù),則會(huì)創(chuàng)建錯(cuò)誤條件,如果除數(shù)參數(shù)為零。我們有三個(gè)匿名的功能,提供三種方式來(lái)出它 - 通過(guò)返回一個(gè)值1,通過(guò)發(fā)送一個(gè)除數(shù)2和重新計(jì)算,或通過(guò)返回1。
創(chuàng)建一個(gè)名為main.lisp一個(gè)新的源代碼文件,并在其中輸入如下代碼:
(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."))
當(dāng)執(zhí)行代碼,它返回以下結(jié)果:
error signaled: denominator is zero Value: 1 error signaled: denominator is zero Value: 5 error signaled: denominator is zero Done.
除了“系統(tǒng)狀態(tài)”,如上文所討論,普通的LISP還提供了各種功能,其可被稱為信令錯(cuò)誤。當(dāng)信號(hào)實(shí)現(xiàn)相關(guān)處理錯(cuò)誤。
下表提供了常用功能的信令警告,休息,非致命和致命的錯(cuò)誤。
用戶程序指定一個(gè)錯(cuò)誤信息(字符串)。該函數(shù)處理這個(gè)消息,并且可能/可能不會(huì)顯示給用戶。
錯(cuò)誤信息應(yīng)該通過(guò)應(yīng)用的格式化功能進(jìn)行構(gòu)造,不應(yīng)該在開(kāi)頭或結(jié)尾包含一個(gè)換行符,也無(wú)需指明錯(cuò)誤,如LISP系統(tǒng)將根據(jù)其喜好的樣式利用這些服務(wù)。
SL No. | 函數(shù)和說(shuō)明 |
---|---|
1 |
error format-string &rest args 它標(biāo)志著一個(gè)致命的錯(cuò)誤。這是不可能從這種錯(cuò)誤的繼續(xù);這樣的錯(cuò)誤將永遠(yuǎn)不會(huì)返回到其調(diào)用者。 |
2 |
cerror continue-format-string error-format-string &rest args 它發(fā)出錯(cuò)誤信號(hào),并進(jìn)入調(diào)試器。但是,它允許程序從調(diào)試器解決錯(cuò)誤之后繼續(xù)。 |
3 |
warn format-string &rest args 它打印一條錯(cuò)誤消息,但一般不會(huì)進(jìn)入調(diào)試 |
4 |
break &optional format-string &rest args 它打印的消息,并直接進(jìn)入調(diào)試器,而不允許攔截由編程錯(cuò)誤處理設(shè)施的任何可能性 |
示例
在這個(gè)例子中,階乘函數(shù)計(jì)算一個(gè)數(shù)階乘;但是,如果參數(shù)為負(fù),它拋出一個(gè)錯(cuò)誤條件。
創(chuàng)建一個(gè)名為main.lisp一個(gè)新的源代碼文件,并在其中輸入如下代碼:
(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))
當(dāng)執(zhí)行代碼,它返回以下結(jié)果:
120 *** - -1 is a negative number.