LISP-오류 처리
공통 LISP 용어에서 예외를 조건이라고합니다.
사실 조건은 전통적인 프로그래밍 언어의 예외보다 더 일반적입니다. condition 다양한 수준의 함수 호출 스택에 영향을 미칠 수있는 발생, 오류 여부를 나타냅니다.
LISP의 조건 처리 메커니즘은 호출 스택의 상위 수준 코드가 작업을 계속할 수있는 동안 조건이 경고 신호 (예 : 경고 인쇄)에 사용되는 방식으로 이러한 상황을 처리합니다.
LISP의 상태 처리 시스템은 세 부분으로 구성됩니다.
- 조건 신호
- 조건 처리
- 프로세스 다시 시작
조건 처리
여기서 개념을 설명하기 위해 0으로 나누기 조건에서 발생하는 조건을 처리하는 예를 들어 보겠습니다.
조건을 처리하려면 다음 단계를 수행해야합니다.
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− 조건 핸들러는 신호를받은 조건을 처리하는 데 사용되는 코드입니다. 일반적으로 오류 함수를 호출하는 상위 수준 함수 중 하나로 작성됩니다. 조건이 시그널링되면 시그널링 메커니즘은 조건의 클래스를 기반으로 적절한 핸들러를 검색합니다.
각 핸들러는-
- 처리 할 수있는 조건의 유형을 나타내는 유형 지정자
- 단일 인수 인 조건을 취하는 함수
조건이 시그널링되면 시그널링 메커니즘은 조건 유형과 호환되는 가장 최근에 설정된 핸들러를 찾아 해당 기능을 호출합니다.
매크로 handler-case조건 핸들러를 설정합니다. 핸들러 케이스의 기본 형태-
(handler-case expression error-clause*)
각 오류 절은 다음과 같은 형식입니다.
condition-type ([var]) code)
Restarting Phase
이것은 실제로 프로그램을 오류로부터 복구하는 코드이며, 조건 핸들러는 적절한 재시작을 호출하여 조건을 처리 할 수 있습니다. 재시작 코드는 일반적으로 중간 수준 또는 하위 수준 함수에 배치되고 조건 핸들러는 애플리케이션의 상위 수준에 배치됩니다.
그만큼 handler-bind매크로를 사용하면 다시 시작 기능을 제공 할 수 있으며 함수 호출 스택을 해제하지 않고 하위 수준 기능을 계속할 수 있습니다. 즉, 제어 흐름은 여전히 하위 수준 기능에 있습니다.
기본 형태 handler-bind 다음과 같습니다-
(handler-bind (binding*) form*)
각 바인딩은 다음 목록입니다.
- 조건 유형
- 한 인수의 핸들러 함수
그만큼 invoke-restart 매크로는 지정된 이름을 인수로 사용하여 가장 최근에 바인딩 된 재시작 함수를 찾아 호출합니다.
여러 번 다시 시작할 수 있습니다.
예
이 예에서는 divisor 인수가 0 인 경우 오류 조건을 생성하는 division-function이라는 함수를 작성하여 위의 개념을 설명합니다. 값 1을 반환하거나, 제수 2를 보내고 다시 계산하거나, 1을 반환하는 세 가지 방법을 제공하는 세 가지 익명 함수가 있습니다.
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.
위에서 설명한 'Condition System'외에도 Common LISP는 오류 신호를 위해 호출 될 수있는 다양한 기능을 제공합니다. 그러나 신호를받을 때 오류 처리는 구현에 따라 다릅니다.
LISP의 오류 신호 기능
다음 표는 경고, 중단, 치명적이지 않은 오류 및 치명적 오류를 알리는 일반적으로 사용되는 기능을 제공합니다.
사용자 프로그램은 오류 메시지 (문자열)를 지정합니다. 함수는이 메시지를 처리하고 사용자에게 표시하거나 표시하지 않을 수 있습니다.
오류 메시지는 다음을 적용하여 구성해야합니다. format LISP 시스템이 선호하는 스타일에 따라 처리하므로 시작 또는 끝에 개행 문자를 포함하지 않아야하며 오류를 표시 할 필요가 없습니다.
Sr. 아니. | 기능 및 설명 |
---|---|
1 |
error 형식 문자열 및 나머지 인수 치명적인 오류를 나타냅니다. 이런 종류의 오류에서 계속하는 것은 불가능합니다. 따라서 오류는 호출자에게 반환되지 않습니다. |
2 |
cerror 계속 형식 문자열 오류 형식 문자열 및 나머지 인수 오류를 알리고 디버거로 들어갑니다. 그러나 오류를 해결 한 후 디버거에서 프로그램을 계속할 수 있습니다. |
삼 |
warn 형식 문자열 및 나머지 인수 오류 메시지를 인쇄하지만 일반적으로 디버거로 이동하지 않습니다. |
4 |
break& 옵션 형식 문자열 및 나머지 인수 프로그래밍 된 오류 처리 기능에 의한 가로 채기 가능성없이 메시지를 인쇄하고 디버거로 직접 이동합니다. |
예
이 예에서 factorial 함수는 숫자의 계승을 계산합니다. 그러나 인수가 음수이면 오류 조건이 발생합니다.
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.