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.