Common Lisp – use the clos class instance as the hash table key?
I have the following courses:
(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"))
Now, I want to use this class as a key for the hash table The address of the instance can be easily compared with Eq The problem is that this class may have multiple instances of the same class, and I want the hash table to recognize it as a key
Therefore, I try to override the: test parameter of the make hash table function, as follows:
(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)))
Unfortunately, this is not allowed Test needs to be an indicator of one of the EQ, EQL, equal, or equalp functions
One way to solve this problem is to convert the class class to a structure class, but I need to treat it as a class Is there any way to solve this problem?
Solution
You can use a more extensible hash table library, as described in coredump's answer, but you can also use the Common Lisp approach to symbols: you can practice them In this case, you only need an appropriate internship function that needs enough categories to generate specification instances and uses a hash table to store them For example, with a simplified category:
(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))))))
You can then call intern category with the same parameters and return the same object, which you can safely use as a hash table key:
(eq (intern-category "foo" 45) (intern-category "foo" 45)) ;=> T