2013-03-18 83 views
2

我在空閒時間學習Common Lisp並對條件系統有疑問。Common Lisp重新啓動條件綁定

當我們在通用lisp中處理錯誤時,我們在處理程序中指定錯誤類型以確定處理哪個錯誤。在引發和處理錯誤之間,我可以進行一些重新啓動(例如重新啓動的情況下),但我無法在重新啓動時指定錯誤類型。

例如,假設我有一個函數,它接受一個字符串和一個流,將字符串發送到流並從流讀取響應並將其返回。假設如果我的消息是錯誤的,我從流中讀取錯誤響應。我想發出一個錯誤並綁定重啓,要求新的消息是這樣的:

(defun process-message (stream raw-message) 
    (let ((response (get-response stream raw-message))) 
    (restart-case 
     (when (response-error-p response) 
      (error 'message-error :text response)) 
     (change-raw-message (msg) 
     (process-message stream msg))))) 

現在假設消息是複雜的,我在那可以創建一些參數的消息更高的水平得到了另一個功能send-command並調用過程消息。我想綁定另一個重新啓動recreate-command-message,如果'message-error獲取,允許用戶從參數發送新命令。此重新啓動可能會在重新啓動的情況下在process-message,但它不完全正確,因爲process-message不應該知道如send-command這樣的高級功能,並且返回值可能會有所不同。

但現在的流錯誤(如EOF等)將被拋出拋recreate-command-message如果插座將無法通過recreate-command-message重啓將在一些超高層socket-error處理程序,並重新啓動這將是無用的地道錯誤。

這是一個程序設計問題,應該設計一個程序來避免這樣的問題,或者我找不到如何將重新啓動綁定到錯誤類型,或者我無法正確理解條件系統?

謝謝。

+0

你應該學習[關於重新啓動的PCL章節](http://gigamonkeys.com/book/beyond-exception-handling-conditions-and-restarts.html) - 它會給你所有必要的答案。 – 2013-03-18 06:47:28

+0

本章有一個錯誤類型和多次重新啓動的例子。這是關於多種錯誤類型和一次重新啓動。該章中的示例可以稍微修改以面對此問題。假設日誌分析器可以解析多種日誌格式。除了處理程序綁定之外,還會有另一次重新啓動,允許指定日誌格式。現在,使用日誌分析器的代碼將處理解析錯誤和從打開文件中導出文件錯誤。因此,當我們處理文件錯誤時,允許我們更改日誌文件類型的重新啓動將可用,但完全沒用,並且在最壞情況下是有害的。 – JustAnotherCurious 2013-03-18 08:04:21

+0

如果我明白你想要的是什麼:'重啓案例'不處理錯誤 - 它建立重啓點。錯誤由'handler-bind'處理,並在其中指定要處理的錯誤類型,並選擇要應用的錯誤類型。所以你的工作是在你的代碼中正確放置這兩種表單。如果您的意思是重新啓動將以交互方式提供,如果您不處理該錯誤,那麼您是對的。但是,這真的是一個問題嗎? – 2013-03-18 09:02:25

回答

7

也許這會有所幫助:

(define-condition low-level-error (simple-error) 
() 
    (:report (lambda (c s) 
      (format s "low level error.")))) 

(define-condition high-level-error (simple-error) 
() 
    (:report (lambda (c s) 
      (format s "high level error.")))) 

(defun low-level (errorp) 
    (restart-case 
     (when errorp (error 'low-level-error)) 
    (go-on() 
     :report "go on from low-level" 
     t))) 

(defun high-level (high-level-error-p low-level-error-p) 
    (restart-case 
     (progn 
     (when high-level-error-p (error 'high-level-error)) 
     (low-level low-level-error-p)) 
    (go-on() 
     :report "go on from high level" 
     :test (lambda (c) (typep c 'high-level-error)) 
     t))) 

嘗試調用high-level與它的參數不同的值(tnil),並檢查在調試器如果相應的可用重啓滿足您的需求。只有在發出高電平錯誤時才能看到高電平重啓,並且由於更高電平的重啓保持在堆棧上,所以低電平功能不必知道高電平恢復的手段。

對於您的特定用例,如果我理解正確,這將意味着:建立recreate-command-message重新啓動以重新調用send-command中的process-message,並使其僅適用於高級錯誤。

正如您在閱讀上面鏈接的PCL章節Vsevolod後可能知道的那樣,實際上處理這些錯誤,即決定要重新啓動哪個重新啓動,是由handler-bindhandler-case完成的。

+0

謝謝。那:重啓測試關鍵參數就是我想要的。在你的帖子後,我再次看到CLHS並發現它,但我沒有注意到它。謝謝! – JustAnotherCurious 2013-03-18 10:17:37

+0

不客氣 - 很高興幫助。 – danlei 2013-03-18 10:19:09