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
KATZEMehrfach-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 50Fehler 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
99Merke
defstructerzeugt einfache Datensätze mit Zugriffsfunktionendefclass,defgenericunddefmethodbilden CLOS: Methoden gehören zu generischen Funktionen- Mehrfach-Dispatch und
:before/:aftermachen Verhalten flexibel - Das Condition-System bietet
handler-case,handler-bindundrestart-case
Übungsaufgabe
Modelliere Formen als Klassen mit einer generischen Funktion flaeche.
Quiz zur Selbstkontrolle
Wo gehören Methoden in CLOS hin?
- Zu generischen Funktionen, nicht zu Klassen (richtig)
- In die Klasse
- In Strukturen
- In Makros
Was macht call-next-method?
- Ruft die nächstpassende Methode (z. B. der Oberklasse) auf (richtig)
- Beendet das Programm
- Startet eine Schleife
- Wirft einen Fehler
Wofür dient handler-case?
- Fehler abfangen und behandeln (richtig)
- Dateien öffnen
- Klassen anlegen
- Zeit messen
Weiter im Kurs
Zurück: Makros und Code als Daten
Weiter: Ein-/Ausgabe, Format und Pakete
Alle Kapitel: Common Lisp im Überblick