Lisp-error-handling
LISP-エラー処理
一般的なLISPの用語では、例外は条件と呼ばれます。
実際、条件は従来のプログラミング言語の例外よりも一般的です。*条件*は、さまざまなレベルの関数呼び出しスタックに影響を与える可能性のある発生、エラー、またはエラーを表すためです。
LISPの条件処理メカニズムは、コールスタックの上位レベルのコードが作業を継続できる一方で、条件を使用して警告を通知する(たとえば、警告を出力する)ようにそのような状況を処理します。
LISPの条件処理システムには3つの部分があります-
- 状態の通知
- 状態の処理
- プロセスを再起動します
条件の処理
ここで概念を説明するために、ゼロ除算条件から生じる条件を処理する例を取り上げましょう。
条件を処理するには、次の手順を実行する必要があります-
- 条件の定義-「条件とは、クラスが条件の一般的な性質を示し、インスタンスデータが条件の通知につながる特定の状況の詳細に関する情報を保持するオブジェクトです」。 +定義条件マクロは、次の構文を持つ条件を定義するために使用されます-
(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))
)
- ハンドラーの記述-条件ハンドラーは、その上で通知された条件を処理するために使用されるコードです。 通常、エラー関数を呼び出す上位レベル関数の1つで記述されます。 条件が通知されると、通知メカニズムは条件のクラスに基づいて適切なハンドラーを検索します。 +各ハンドラーは-
- タイプ指定子。処理できる条件のタイプを示します
- 単一の引数、条件をとる関数 +条件が通知されると、通知メカニズムは、条件タイプと互換性のある最も新しく確立されたハンドラーを見つけ、その関数を呼び出します。 +マクロ handler-case は条件ハンドラーを確立します。 ハンドラーケースの基本形-
(handler-case expression error-clause*)
ここで、各エラー句は次の形式です-
condition-type ([var]) code)
- フェーズの再開 * +これは実際にプログラムをエラーから回復するコードであり、条件ハンドラーは適切な再起動を呼び出すことで条件を処理できます。 通常、再起動コードは中間レベルまたは低レベルの関数に配置され、条件ハンドラーはアプリケーションの上位レベルに配置されます。 + handler-bind マクロを使用すると、再起動関数を提供でき、関数呼び出しスタックを巻き戻さずに下位レベルの関数で続行できます。 言い換えると、制御の流れは依然として下位レベルの機能にあります。 + *handler-bind の基本形式は次のとおりです-
(handler-bind (binding*) form*)
各バインディングは次のリストです-
条件タイプ
1つの引数のハンドラー関数
*invoke-restart* マクロは、指定された名前を引数として使用して、最後にバインドされた再起動関数を見つけて呼び出します。
複数回再起動することができます。
例
この例では、除数引数がゼロの場合にエラー条件を作成するdivision-functionという名前の関数を記述することにより、上記の概念を示します。 値1を返すこと、除数2を送信して再計算すること、または1を返すことの3つの方法を提供する3つの匿名関数があります。
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システムは、優先スタイルに従ってこれらを処理します。
Sr.No. | Function and Description |
---|---|
1 |
致命的なエラーを通知します。 この種のエラーから続行することは不可能です。したがって、エラーは呼び出し元に戻ることはありません。 |
2 |
エラーを通知し、デバッガーに入ります。 ただし、エラーを解決した後、デバッガーからプログラムを続行できます。 |
3 |
エラーメッセージを出力しますが、通常はデバッガーには入りません |
4 |
プログラムされたエラー処理機能によるインターセプトの可能性を許可せずに、メッセージを出力し、デバッガーに直接入ります。 |
例
この例では、階乗関数は数値の階乗を計算します。ただし、引数が負の場合、エラー条件が発生します。
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.