2016-08-27 3 views
1

Wenn Slots Klasse Zugriff stattPunktnotation verwenden CLOS Slots für den Zugriff auf

(defmethod get-name ((somebody person) (slot-value somebody 'name)) 
Schreiben

ist es möglich, die Punktnotation aka C++, sonst

(defmethod get-name ((somebody person) somebody.name) ? 

nämlich zu verwenden, wenn es viele sind Slot-Operationen in einer Methode, (slot-value... erzeugt eine Menge Boilerplate-Code.

Ich habe die Antwort heute herausgefunden, und ich schreibe es gerade als Q & A, aber wenn es bessere Lösungen gibt oder es Probleme gibt, die ich mit meiner Lösung erwarten sollte, zögern Sie nicht, neue Antworten oder Kommentare hinzuzufügen.

Antwort

2

Sie sollten nicht Accessoren von Hand schreiben, noch slot-value verwenden (außerhalb von Objektlebenszyklus-Funktionen, wo Die Accessoren wurden möglicherweise noch nicht erstellt).Verwenden Sie die Klasse Schlitzoptionen statt:

(defclass foo() 
    ((name :reader foo-name 
     :initarg :name) 
    (bar :accessor foo-bar 
     :initarg :bar))) 

Jetzt können Sie die Namen Accessoren verwenden:

(defun example (some-foo new-bar) 
    (let ((n (foo-name some-foo)) 
     (old-bar (foo-bar some-foo))) 
    (setf (foo-bar some-foo) new-bar) 
    (values n old-bar))) 

Oft Sie Ihre Klassen „unveränderlich“ zu sein, würden Sie :reader statt :accessor verwenden dann , die nur den Leser erzeugt, nicht die setf-Erweiterung.

+0

Danke, das ist in der Tat, was ich gesucht habe. Ich denke, ich habe das entsprechende Kapitel in Practical Common Lisp einfach nicht weit genug gelesen. Also wusste ich auch nicht, dass es Makros mit und Makros gibt. – Andrei

2

Die einfachste Lösung scheint ein Leser Makro zu sein, die . so überlastet, dass (slot-value somebody 'name) Meine Strategie ist somebody.name als String lesen (wir einen nicht abbreche Makro Zeichen definieren müssen, damit der Leser tut geschrieben werden kann als .somebody.name nicht stoppen Mitte string), und dann verarbeiten die Zeichenfolge die entsprechende (slot-value...

ich zwei Hilfsfunktionen müssen konstruieren:

(defun get-symbol (str) 
    "Make an uppercase symbol" 
    (intern (string-upcase str))) 

(defun split-string (str sep &optional (start 0)) 
    "Split a string into lists given a character separator" 
    (let ((end (position sep str :start start))) 
    (cons (subseq str start end) (if end (split-string str sep (1+ end)))))) 

und dann kann ich meine Leser Makro definieren:

(defun dot-reader (stream char) 
    (declare (ignore char)) 
    (labels ((make-query (list) 
      (let ((car (car list)) 
        (cdr (cdr list))) 
       (if cdr `(slot-value ,(make-query cdr) (quote ,(get-symbol car))) 
        (get-symbol car))))) 
    (make-query (nreverse (split-string (symbol-name (read stream)) #\.))))) 

Schließlich muss ich diese Leser Makro registrieren:

(set-macro-character #\. #'dot-reader t) 

Jetzt ist es möglich, zu schreiben:

(defmethod get-name ((somebody person) .somebody.name) 

oder, wenn name selbst eine Klasse,

(defmethod get-name ((somebody person) .somebody.name.first-name) 

Eine Einschränkung ist, dass s-Ausdrücke zwischen t nicht funktioniert er Punkte, sagen

.(get-my-class).name 

wird nicht funktionieren.

+0

Ermöglicht dies immer noch den normalen eintreffenden Punkt, z. '(setq cons '(a. b))'? – Barmar

+0

@Barmar Interessanter Punkt, aber ja, Consing Punkt funktioniert immer noch. Es scheint, dass das Leser-Makro in diesem Fall einfach nicht aufgerufen wird. – Andrei

5

Die Bibliothek access bietet ein Punktnotationsleser-Makro für den Zugriff auf Steckplätze (und Hash-Tabellen und andere Dinge). Nach dem Aktivieren des Reader-Makros durch Aufruf (Zugriff: Enable-Dot-Syntax) können Sie #D verwenden. um auf einen Steckplatznamen mit der in anderen Sprachen beliebten Punktsyntax zuzugreifen.

(defclass person() 
    ((name :initarg :name :reader name))) 

CL-USER> (access:enable-dot-syntax) 
; No values 
CL-USER> (defvar *foo* (make-instance 'person :name "John Smith")) 
*FOO* 
CL-USER> #D*foo* 
#<PERSON #x302001F1E5CD> 
CL-USER> #D*foo*.name 
"John Smith" 

Es gibt auch ein with-dot Makro, wenn Sie nicht wollen, einen Leser Makro verwenden

CL-USER> (access:with-dot() *foo*.name) 
"John Smith" 
+0

Vielen Dank, dass Sie auf diese Bibliothek hingewiesen haben. Leider kann ich es jetzt nicht versuchen, (ql: quickload "access") führt zu Fehlern (ich werde mich schwer tun, mich an das letzte Mal zu erinnern, dass pacman -S zu einem Fehler geführt hat). Ich werde auf Ihre Antwort zurückkommen, sobald ich diese gelöst habe. – Andrei

+0

Anscheinend gab es einige Probleme mit der Vorwärtskompatibilität. Das Aktualisieren aller Pakete mit (ql: update-all-dists) hat das Problem gelöst und ich kann jetzt die Access-Bibliothek ausprobieren. Ich mag es, weil, na ja, es ist eine Bibliothek und ich muss nichts selbst programmieren und pflegen. Ich mag es weniger für die anstehende Aufgabe. Der Fokus der Bibliothek ist anders als einfach eine Punktnotation für Klassen bereitzustellen und viele Laufzeitüberprüfungen durchzuführen. Der Zugriff auf einen Slot eines Objekts ist 149 Mal langsamer als mein manueller Ansatz, und der Zugriff auf einen Slot eines Slot eines Objekts ist 174 mal langsamer. – Andrei

Verwandte Themen