LISP-エラー処理
一般的なLISPの用語では、例外は条件と呼ばれます。
実際、条件は従来のプログラミング言語の例外よりも一般的です。 condition さまざまなレベルの関数呼び出しスタックに影響を与える可能性のある発生、エラー、またはそうでないことを表します。
LISPの条件処理メカニズムは、条件が警告を通知するために使用されるように(たとえば、警告を出力することによって)そのような状況を処理し、コールスタックの上位レベルのコードはその作業を続行できます。
LISPの条件処理システムには3つの部分があります-
- 状態の通知
- 状態の処理
- プロセスを再開します
状態の処理
ここで概念を説明するために、ゼロ除算条件から生じる条件を処理する例を取り上げましょう。
状態を処理するには、次の手順を実行する必要があります-
Define the Condition −「条件とは、クラスが条件の一般的な性質を示し、インスタンスデータが条件の通知につながる特定の状況の詳細に関する情報を保持するオブジェクトです」。
条件の定義マクロは、次の構文を持つ条件を定義するために使用されます。
(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))
)
Writing the Handlers−条件ハンドラーは、通知された条件を処理するために使用されるコードです。これは通常、エラー関数を呼び出す高レベルの関数の1つで記述されます。条件が通知されると、通知メカニズムは条件のクラスに基づいて適切なハンドラーを検索します。
各ハンドラーは-で構成されます
- 処理できる条件のタイプを示すタイプ指定子
- 単一の引数を取る関数、条件
条件が通知されると、通知メカニズムは、条件タイプと互換性のある最後に確立されたハンドラーを見つけて、その関数を呼び出します。
マクロ handler-case条件ハンドラーを確立します。ハンドラーケースの基本形式-
(handler-case expression error-clause*)
ここで、各エラー句は次の形式です。
condition-type ([var]) code)
Restarting Phase
これは、実際にプログラムをエラーから回復するコードであり、条件ハンドラーは、適切な再起動を呼び出すことによって条件を処理できます。再起動コードは通常、中レベルまたは低レベルの関数に配置され、条件ハンドラーはアプリケーションの上位レベルに配置されます。
ザ・ 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システムがその好みのスタイルに従ってこれらを処理するため、エラーを示す必要はありません。
シニア番号 | 機能と説明 |
---|---|
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.