2015-11-20 13 views
6

Ho la seguente classe:Utilizzo delle istanze di classe CLOS come chiavi della tabella hash?

(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")) 

Ora, vorrei usare questa classe come una chiave per una hash-table. Gli indirizzi delle istanze possono essere facilmente confrontati con eq. Il problema è, tuttavia, potrebbero esserci più istanze identiche di questa classe e vorrei che anche la tabella hash riconosca questo come una chiave.

Così, stavo cercando di sovrascrivere il :test argomento della funzione make-hash-table in questo modo:

(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))) 

Purtroppo, questo non è permesso. :test deve essere un designatore per una delle funzioni eq, eql, uguale o uguale.

Un modo per risolvere questo sarebbe trasformare la classe category in una struttura, ma ho bisogno che sia una classe. C'è un modo per risolvere questo?

+0

Perché hai bisogno che sia una lezione? – coredump

+0

Vuoi usare * instance * come chiavi, o la classe stessa? –

risposta

6

È possibile utilizzare una libreria tabella hash più estensibile, come spiegato nella risposta di coredump, ma si potrebbe anche usare l'approccio che Common Lisp porta verso simboli: è possibile stagista loro. In questo caso, è sufficiente una funzione di interning appropriata che tenga abbastanza di una categoria per produrre un'istanza canonica e una tabella di hash per archiviarli. Per esempio., Con una semplificata categoria classe:

(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)))))) 

Quindi, è possibile chiamare stagista-categoria gli stessi argomenti e ottenere stesso oggetto retro, che si può tranquillamente utilizzare come chiave tabella di hash:

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

Me ne ricorderò, buon approccio. – coredump

+0

Penso che questa sia una soluzione elegante al mio problema. Grazie! – JNevens

+0

Questo in realtà non risponde alla domanda. Mostra solo che puoi mappare le slot alle conses, che possono essere le tabelle hash 'equalp'. Ma la mia ipotesi migliore è che il ** op ** ne sia consapevole, citando l'uso delle strutture, che le tabelle hash 'equalp' possono annodare altrettanto bene. – acelent

6
  1. Non confrontare i numeri con eq, utilizzare eql o =. Da eq (emphasis mine):

    Gli oggetti che appaiono uguali quando stampati non sono necessariamente eq l'uno con l'altro. [...] Un'implementazione è consentita per fare "copie" di caratteri e numeri in qualsiasi momento. L'effetto è che Common Lisp rende non garantisce che eq sia vero anche se entrambi i suoi argomenti sono "la stessa cosa" se quella cosa è un carattere o numero.

  2. È possibile utilizzare la libreria genhash. In primo luogo, si definisce una nuova funzione hash (vedi anche sxhash) e di una funzione di test per il tipo e si associa ad una test di designatore:

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

    Quindi, è possibile definire una nuova tabella:

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

Molte implementazioni Common Lisp forniscono estensioni allo standard ANSI Common Lisp per supportare diverse funzioni di test e di hash (e molto altro).

CL-CUSTOM-HASH-TABLE è un livello di compatibilità.

Problemi correlati