5

Common Lisp の「Practical Common Lisp」の例外処理の章を何日も読んでいますが、サンプルと説明にとても混乱しています。以下は私のテストサンプルです。

  1. 条件定義

    (define-condition evenp-error (error) 
      ((text :initarg :text :reader text)))
    
  2. 奇数を出力する関数を定義する

    (defun filter-evenp (lst)
      (dolist (x lst)
        (if (not (evenp x)) 
          (print x)
          (error 'evenp-error :text x))))
    
  3. リスタート機能

    (defun skip-evenp (c) (invoke-start 'skip-evenp))
    
  4. ケースの再起動

    (restart-case (filter-evenp (list 1 2 3 4 5))
      (skip-evenp () nil))
    

私がしたいのは、すべての奇数を出力し、偶数のエラーをスキップすることだけです。私のサンプルの何が問題なのですか? 誰か助けて?よろしくお願いします!

4

2 に答える 2

7

Practical Common Lispは非常に詳細ですが、条件システムに慣れるまでに時間がかかることは事実です。Kent Pitman の記事: Exceptional Situations in Lisp およびCondition Handling in the Lisp Language Familyに興味があるかもしれません。

これらは、条件システムとは何か、なぜ必要なのかで参照されています。. このWikibooks エントリや C2 wiki のCommonLispConditionSystemエントリなど、他にも多くの参考文献があります。

再始動の定義

ARESTART-CASEは基本的に次のように言います。

このフォームを実行しますが、それが条件を通知するかどうかは気にしません。しかし、そうで、その状況から回復したい場合は、問題を回避できるさまざまな方法があります (再試行、無視など)。

通常、コール ポイントで呼び出されているコードのエラーから回復する方法を言うことはできません。言い換えれば、代替パスを提供するためにfilter-evenpコードを順番にラップする必要があります。あなたの例では、再起動の確立中にエラーを通知するrestart-caseを使用するだけで十分です。CERRORCONTINUE

(if (evenp x)
  (cerror "Ignore even number" 'evenp-error :text x) 
  (print x))

(cerror ...)演習として、明示的な構造に置き換えることができますrestart-case

次に、コードをテストすると、デバッガーがポップアップ表示され、CONTINUE再起動が表示されるはずです。独自の再起動を定義した場合は、別の名前を付けることができます。

再起動の呼び出し

あなたの機能では、この時点で確立されていない再起動を呼び出していましたが、再起動と機能の両方の名前付けskip-evenpに混乱していたと思います。skip-evenp

すべきことは、再起動を呼び出してエラーを処理することです。

ここでは、エラーを通知するコードを続行する必要があるため、実行スタックを巻き戻したくありません。そのため、使用する必要がありますHANDLER-BIND

(handler-bind ((evenp-error (lambda (e) (invoke-restart 'continue))))
  (filter-evenp '(1 2 3 4)))
1
3    

もちろん、匿名ラムダをカスタム関数に抽出することもできます。

于 2016-03-23T12:56:22.643 に答える
5

RESTART-CASE実行を再開する場所に配置する必要があります。

(defun filter-evenp (lst)
  (dolist (x lst)
    (restart-case
        (if (not (evenp x))
            (print x)
            (error 'evenp-error :text x))
      (skip-evenp () nil))))

次にHANDLER-BIND、エラーを処理するために使用する必要があります。

(handler-bind ((evenp-error #'skip-evenp))
  (filter-evenp (list 1 2 3 4 5)))
于 2016-03-23T12:52:07.583 に答える