我有以下课程:
(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"))
现在,我想将此类用作哈希表的键。可以轻松地将实例的地址与eq
进行比较。但问题是,这个category
类可能有多个相同的实例,我希望哈希表也将其识别为关键字。
所以,我试图像这样覆盖:test
函数的make-hash-table
参数:
(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)))
不幸的是,这是不允许的。 :test
需要成为eq,eql,equal或equalp函数之一的指示符。
解决这个问题的一种方法是将类category
转换为结构,但我需要它成为一个类。有什么方法可以解决这个问题吗?
答案 0 :(得分:6)
请勿将数字与eq
进行比较,请使用eql
或=
。来自eq
(强调我的):
打印时看起来相同的对象不一定是彼此相等的。 [...] 允许实现随时制作字符和数字的“副本”。效果是Common Lisp使无法保证eq为真,即使它的两个参数都是“相同的东西”,如果那个是字符或数字< /强>
您可以使用genhash
库。首先,为类型定义一个新的哈希函数(另请参阅sxhash
)和测试函数,并将其与测试指示符相关联:
(genhash:register-test-designator
'category=
(lambda (category) <hashing>)
(lambda (a b)
(and (equal ... ...)
(= ... ...)
(= ... ...))))
然后,您可以定义一个新表:
(genhash:make-generic-hashtable :test 'category=)
答案 1 :(得分:6)
您可以使用更可扩展的哈希表库,如coredump的答案中所述,但您也可以使用Common Lisp对符号采用的方法:您可以实习它们。在这种情况下,您只需要一个适当的实习函数,该函数需要足够的类别来生成规范实例,并使用哈希表来存储它们。例如,使用简化的类别类:
(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))))))
然后,您可以使用相同的参数调用 intern-category 并返回相同的对象,您可以安全地将其用作哈希表键:
(eq (intern-category "foo" 45)
(intern-category "foo" 45))
;=> T
答案 2 :(得分:6)
许多Common Lisp实现提供ANSI Common Lisp标准的扩展,以支持不同的测试和散列函数(以及更多)。
CL-CUSTOM-HASH-TABLE是兼容层。