LISP - Hata İşleme
Ortak LISP terminolojisinde, istisnalara koşullar denir.
Aslında, koşullar geleneksel programlama dillerindeki istisnalardan daha geneldir, çünkü condition işlev çağrı yığınının çeşitli düzeylerini etkileyebilecek herhangi bir oluşumu, hatayı veya olmadığını gösterir.
LISP'deki durum işleme mekanizması, bu tür durumları, çağrı yığınındaki üst düzey kodun çalışmaya devam ederken uyarı sinyali vermek için (örneğin bir uyarı yazdırarak) koşulların kullanıldığı bir şekilde ele alır.
LISP'deki durum işleme sistemi üç bölümden oluşur -
- Bir koşula işaret etme
- Durumu ele almak
- İşlemi yeniden başlatın
Bir Durumu Ele Alma
Buradaki kavramları açıklamak için sıfıra bölme koşulundan kaynaklanan bir durumu ele alalım.
Bir durumu ele almak için aşağıdaki adımları atmanız gerekir -
Define the Condition - "Bir koşul, sınıfı koşulun genel yapısını gösteren ve örnek verileri, koşulun sinyallenmesine yol açan belirli koşulların ayrıntıları hakkında bilgi taşıyan bir nesnedir".
Koşul tanımlama makrosu, aşağıdaki sözdizimine sahip bir koşulu tanımlamak için kullanılır:
(define-condition condition-name (error)
((text :initarg :text :reader text))
)
MAKE-CONDITION makrosu ile yeni koşul nesneleri oluşturulur; bu, yeni koşulun yuvalarını, :initargs argüman.
Örneğimizde, aşağıdaki kod koşulu tanımlar -
(define-condition on-division-by-zero (error)
((message :initarg :message :reader message))
)
Writing the Handlers- bir koşul işleyici, üzerinde işaret edilen koşulu işlemek için kullanılan bir koddur. Genellikle, hata yapma işlevini çağıran üst düzey işlevlerden birinde yazılır. Bir koşul sinyal verildiğinde, sinyalleme mekanizması koşulun sınıfına göre uygun bir işleyici arar.
Her işleyici şunlardan oluşur:
- İşleyebileceği koşul türünü belirten tür belirticisi
- Tek bir argüman alan bir işlev, koşul
Bir koşul sinyallendiğinde, sinyalleme mekanizması, koşul tipiyle uyumlu en son kurulan işleyiciyi bulur ve onun işlevini çağırır.
Makro handler-casebir koşul işleyici kurar. İşleyici vakasının temel formu -
(handler-case expression error-clause*)
Nerede, her bir hata cümlesinin biçimi -
condition-type ([var]) code)
Restarting Phase
Bu, programınızı hatalardan kurtaran koddur ve koşul işleyicileri daha sonra uygun bir yeniden başlatma çağrısı yaparak bir koşulu işleyebilir. Yeniden başlatma kodu genellikle orta düzey veya düşük düzey işlevlere yerleştirilir ve koşul işleyicileri uygulamanın üst düzeylerine yerleştirilir.
handler-bindmakrosu, bir yeniden başlatma işlevi sağlamanıza ve işlev çağrı yığınını çözmeden alt düzey işlevlerde devam etmenize olanak tanır. Başka bir deyişle, kontrol akışı yine de alt düzey işlevde olacaktır.
Temel formu handler-bind aşağıdaki gibidir -
(handler-bind (binding*) form*)
Her bağlamanın aşağıdakilerin bir listesi olduğu yerlerde -
- bir koşul türü
- bir bağımsız değişkenin işleyici işlevi
invoke-restart makrosu, argüman olarak belirtilen adla en son bağlanan yeniden başlatma işlevini bulur ve çağırır.
Birden çok yeniden başlatmaya sahip olabilirsiniz.
Misal
Bu örnekte, yukarıdaki kavramları, bölen bağımsız değişkeni sıfırsa bir hata koşulu oluşturacak bölme işlevi adlı bir işlev yazarak gösteriyoruz. Bundan çıkmanın üç yolunu sağlayan üç anonim fonksiyonumuz var - 1 değerini döndürerek, bölen 2'yi göndererek ve yeniden hesaplayarak veya 1'i döndürerek.
Main.lisp adında yeni bir kaynak kodu dosyası oluşturun ve içine aşağıdaki kodu yazın.
(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."))
Kodu çalıştırdığınızda, aşağıdaki sonucu döndürür -
error signaled: denominator is zero
Value: 1
error signaled: denominator is zero
Value: 5
error signaled: denominator is zero
Done.
Yukarıda tartışıldığı gibi "Koşul Sistemi" dışında, Ortak LISP ayrıca bir hatayı bildirmek için çağrılabilecek çeşitli işlevler de sağlar. Bununla birlikte, sinyal verildiğinde bir hatanın ele alınması uygulamaya bağlıdır.
LISP'de Hata Sinyalleme İşlevleri
Aşağıdaki tablo, uyarıları, kesintileri, önemli olmayan ve önemli hataları gösteren yaygın olarak kullanılan işlevleri sağlar.
Kullanıcı programı bir hata mesajı (bir dizi) belirtir. İşlevler bu mesajı işler ve bunu kullanıcıya gösterebilir / göstermeyebilir.
Hata mesajları, format LISP sistemi bunları tercih ettiği stile göre halledeceğinden, başında veya sonunda bir satırsonu karakteri içermemelidir ve hatayı göstermesi gerekmez.
Sr.No. | İşlev ve Açıklama |
---|---|
1 | error format-string & rest argümanları Ölümcül bir hatayı işaret ediyor. Bu tür bir hatadan devam etmek imkansızdır; bu nedenle hata asla arayana geri dönmez. |
2 | cerror Continue-format-string error-format-string & rest argümanları Bir hatayı bildirir ve hata ayıklayıcıya girer. Ancak, hatayı çözdükten sonra programın hata ayıklayıcıdan devam etmesine izin verir. |
3 | warn format-string & rest argümanları bir hata mesajı yazdırır ancak normalde hata ayıklayıcıya gitmez |
4 | break& isteğe bağlı biçim dizesi ve kalan bağımsız değişkenler Mesajı yazdırır ve programlanmış hata işleme araçları tarafından herhangi bir müdahale olasılığına izin vermeden doğrudan hata ayıklayıcıya gider |
Misal
Bu örnekte, faktöriyel işlevi bir sayının faktöriyelini hesaplar; ancak, bağımsız değişken negatifse, bir hata durumu ortaya çıkarır.
Main.lisp adında yeni bir kaynak kodu dosyası oluşturun ve içine aşağıdaki kodu yazın.
(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))
Kodu çalıştırdığınızda, aşağıdaki sonucu döndürür -
120
*** - -1 is a negative number.