EMZETT.
Login

Kurz: (let ((p (make-person :name “Mia” :alter 17))) (format t “~a a%” (person-name p) (person-alter p)) (setf (person-alter p) 18) (push “Lesen” (person-hobbys p)) (format t “~a a%” (person-alter p) (person-hobbys p)) (format t.

Teil des Kurses Common Lisp

Kapitel 5 von 8 im Kurs Common Lisp. Mit Fortschritt, Quiz und Zertifikat auf der Lernseite.

defstruct

(defstruct person
  name
  (alter 0)
  (hobbys nil))
 
(let ((p (make-person :name "Mia" :alter 17)))
  (format t "~a ~a~%" (person-name p) (person-alter p))
  (setf (person-alter p) 18)
  (push "Lesen" (person-hobbys p))
  (format t "~a ~a~%" (person-alter p) (person-hobbys p))
  (format t "~a~%" (person-p p))
  (let ((kopie (copy-person p)))
    (setf (person-name kopie) "Tom")
    (format t "~a ~a~%" (person-name p) (person-name kopie)))
  (format t "~a~%" p))

Ausgabe:

Mia 17
18 (Lesen)
T
Mia Tom
#S(PERSON :NAME Mia :ALTER 18 :HOBBYS (Lesen))

Klassen mit CLOS

Das Common Lisp Object System ist eines der mächtigsten Objektsysteme. Methoden gehören nicht zu Klassen, sondern zu generischen Funktionen:

(defclass tier ()
  ((name :initarg :name :accessor tier-name)
   (laut :initarg :laut :initform "..." :accessor tier-laut)))
 
(defclass hund (tier)
  ((rasse :initarg :rasse :initform "Mischling" :reader hund-rasse))
  (:default-initargs :laut "Wau"))
 
(defclass katze (tier) ()
  (:default-initargs :laut "Miau"))
 
(defgeneric vorstellen (tier)
  (:documentation "Stellt das Tier vor."))
 
(defmethod vorstellen ((tier tier))
  (format t "~a sagt ~a~%" (tier-name tier) (tier-laut tier)))
 
(defmethod vorstellen ((h hund))
  (call-next-method)
  (format t "  Rasse: ~a~%" (hund-rasse h)))
 
(let ((tiere (list (make-instance 'hund :name "Rex" :rasse "Schäferhund")
                   (make-instance 'katze :name "Mimi"))))
  (mapc #'vorstellen tiere)
  (format t "~a~%" (typep (first tiere) 'tier))
  (format t "~a~%" (class-name (class-of (second tiere)))))

Ausgabe:

Rex sagt Wau
  Rasse: Schäferhund
Mimi sagt Miau
T
KATZE

Mehrfach-Dispatch und Hilfsmethoden

Methoden können auf mehreren Argumenten verzweigen:

(defclass form () ())
(defclass kreis (form) ())
(defclass rechteck (form) ())
 
(defgeneric kollision (a b))
(defmethod kollision ((a kreis) (b kreis)) "Kreis trifft Kreis")
(defmethod kollision ((a kreis) (b rechteck)) "Kreis trifft Rechteck")
(defmethod kollision ((a rechteck) (b form)) "Rechteck trifft irgendeine Form")
 
(format t "~a~%" (kollision (make-instance 'kreis) (make-instance 'kreis)))
(format t "~a~%" (kollision (make-instance 'kreis) (make-instance 'rechteck)))
(format t "~a~%" (kollision (make-instance 'rechteck) (make-instance 'kreis)))
 
(defclass konto () ((stand :initform 0 :accessor stand)))
(defmethod einzahlen :before ((k konto) betrag)
  (format t "vor Einzahlung ~a~%" betrag))
(defmethod einzahlen ((k konto) betrag) (incf (stand k) betrag))
(defmethod einzahlen :after ((k konto) betrag)
  (format t "danach ~a~%" (stand k)))
 
(let ((k (make-instance 'konto)))
  (einzahlen k 50))

Ausgabe:

Kreis trifft Kreis
Kreis trifft Rechteck
Rechteck trifft irgendeine Form
vor Einzahlung 50
danach 50

Fehler und das Condition-System

Das Condition-System trennt Fehler melden von Fehler behandeln und erlaubt Wiederaufnahme (Restarts):

(define-condition eigener-fehler (error)
  ((code :initarg :code :reader fehler-code))
  (:report (lambda (c s) (format s "Eigener Fehler Nr. ~a" (fehler-code c)))))
 
(defun gefaehrlich (n)
  (cond ((< n 0) (error 'eigener-fehler :code n))
        ((= n 0) (error "Null nicht erlaubt"))
        (t (/ 100 n))))
 
(handler-case (gefaehrlich -5)
  (eigener-fehler (e) (format t "gefangen: ~a~%" e)))
 
(handler-case (gefaehrlich 0)
  (error (e) (format t "Fehler: ~a~%" e)))
 
(handler-case (/ 1 0)
  (division-by-zero () (format t "durch null~%")))
 
(format t "~a~%" (handler-case (gefaehrlich 4) (error () :fehler)))
 
(unwind-protect
    (handler-case (error "kaputt") (error () (format t "behandelt~%")))
  (format t "aufräumen~%"))
 
(defun mit-restart (n)
  (restart-case (error "Problem ~a" n)
    (nimm-null () 0)
    (nimm-wert (w) w)))
(format t "~a~%" (handler-bind ((error (lambda (c) (declare (ignore c)) (invoke-restart 'nimm-wert 99))))
                   (mit-restart 1)))

Ausgabe:

gefangen: Eigener Fehler Nr. -5
Fehler: Null nicht erlaubt
durch null
25
behandelt
aufräumen
99

Merke

  • defstruct erzeugt einfache Datensätze mit Zugriffsfunktionen
  • defclass, defgeneric und defmethod bilden CLOS: Methoden gehören zu generischen Funktionen
  • Mehrfach-Dispatch und :before/:after machen Verhalten flexibel
  • Das Condition-System bietet handler-case, handler-bind und restart-case

Übungsaufgabe

Modelliere Formen als Klassen mit einer generischen Funktion flaeche.

Quiz zur Selbstkontrolle

Weiter im Kurs

Zurück: Makros und Code als Daten

Weiter: Ein-/Ausgabe, Format und Pakete

Alle Kapitel: Common Lisp im Überblick