2015-11-20 12 views
6

Ich habe die folgende Klasse:CLOS-Klasseninstanzen als Hash-Tabellenschlüssel verwenden?

(defclass category() 
    ((cat-channel-name 
    :accessor cat-channel-name :initarg :cat-channel-name :initform "" :type string 
    :documentation "Name of the channel of this category") 
    (cat-min 
    :accessor cat-min :initarg :min :initform 0 :type number 
    :documentation "Mininum value of category") 
    (cat-max 
    :accessor cat-max :initarg :max :initform 1 :type number 
    :documentation "Maximum value of category")) 
    (:documentation "A category")) 

Nun würde Ich mag diese Klasse als Schlüssel für eine Hash-Tabelle verwenden. Die Adressen von Instanzen können leicht mit verglichen werden. Das Problem ist jedoch, dass es mehrere identische Instanzen dieser category Klasse geben könnte und ich möchte, dass die Hash-Tabelle dies auch als Schlüssel erkennt.

Also, ich versuche, das :test Argument der make-hash-table Funktion wie diese zu überschreiben:

(make-hash-table :test #'(lambda (a b) (and (equal (cat-channel-name a) (cat-channel-name b)) 
              (eq (cat-min a) (cat-min b)) 
              (eq (cat-max a) (cat-max b))) 

Leider ist dies nicht erlaubt ist. :test muss ein Bezeichner für eine der Funktionen eq, eql, equal oder equalp sein.

Eine Möglichkeit, dies zu lösen, wäre die Klasse category in eine Struktur zu verwandeln, aber ich brauche es eine Klasse zu sein. Kann ich das irgendwie lösen?

+0

Warum brauchen Sie eine Klasse? – coredump

+0

Möchten Sie * Instanzen * als Schlüssel oder die Klasse selbst verwenden? –

Antwort

6

Sie können eine erweiterbare Hash-Tabelle Bibliothek verwenden, wie in coredump Antwort erklärt, aber man könnte auch den Ansatz verwenden, die Common Lisp in Richtung Symbole nimmt: Sie können intern sie. In diesem Fall benötigen Sie nur eine entsprechende Internierungsfunktion, die eine Kategorie zum Erstellen einer kanonischen Instanz und eine Hash-Tabelle zum Speichern derselben enthält. Z.B., Mit einer vereinfachten Kategorie Klasse:

(defclass category() 
    ((name :accessor cat-name :initarg :name) 
    (number :accessor cat-number :initarg :number))) 

(defparameter *categories* 
    (make-hash-table :test 'equalp)) 

(defun intern-category (name number) 
    (let ((key (list name number))) 
    (multiple-value-bind (category presentp) 
     (gethash key *categories*) 
     (if presentp category 
      (setf (gethash key *categories*) 
       (make-instance 'category 
           :name name 
           :number number)))))) 

Dann können Sie intern-Kategorie mit den gleichen Argumenten aufrufen und das gleiche Objekt zurück, das Sie sicher als Hash-Tabellenschlüssel verwenden können:

(eq (intern-category "foo" 45) 
    (intern-category "foo" 45)) 
;=> T 
+0

Ich werde mich daran erinnern, nette Annäherung. – coredump

+0

Ich denke, das ist eine elegante Lösung für mein Problem. Vielen Dank! – JNevens

+0

Das beantwortet die Frage nicht wirklich. Es zeigt nur, dass Sie Slots für Conseys zuordnen können, welche 'equalp' Hashtabellen grok sind. Aber meine beste Vermutung ist, dass der ** op ** das weiß, indem er die Verwendung von Strukturen erwähnt, die "gleiche" Hashtabellen genauso gut erreichen können. – acelent

6
  1. nicht vergleichen Zahlen mit eq, eql oder = verwenden. Von (Hervorhebung von mir):

    Objekte, die beim Drucken gleich aussehen, sind nicht unbedingt äquivalent zueinander. [...] Eine Implementierung darf "Kopien" von Zeichen und Zahlen jederzeit machen. Der Effekt ist, dass Common Lisp macht keine Garantie, dass eq ist wahr auch dann, wenn beide Argumente sind "die gleiche Sache", wenn das Ding ein Zeichen oder Nummer ist.

  2. Sie können die Bibliothek genhash verwenden. Erstens gibt es eine neue Hash-Funktion definieren (siehe auch sxhash) und eine Testfunktion für Ihre Art und Sie verbinden sie mit einem Test Bezeichner:

    (genhash:register-test-designator 
        'category= 
        (lambda (category) <hashing>) 
        (lambda (a b) 
        (and (equal ... ...) 
         (= ... ...) 
         (= ... ...)))) 
    

    Dann können Sie eine neue Tabelle definieren:

    (genhash:make-generic-hashtable :test 'category=) 
    
6

Viele Common Lisp-Implementierungen bieten Erweiterungen des ANSI Common Lisp-Standard verschiedene Test- und Hash-Funktionen (und viel mehr) zu unterstützen.

CL-CUSTOM-HASH-TABLE ist eine Kompatibilitätsschicht.