7

我有以下课程:

(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变成一个结构,但我需要它成为一个类。有什么办法可以解决这个问题吗?

4

3 回答 3

7

许多 Common Lisp 实现提供了对 ANSI Common Lisp 标准的扩展,以支持不同的测试和散列函数(以及更多)。

CL-CUSTOM-HASH-TABLE是一个兼容层。

于 2015-11-20T17:34:18.337 回答
6
  1. 不要将数字与eqeql或进行比较=。来自eq(强调我的):

    打印时看起来相同的对象不一定彼此相等。[...] 任何时候都允许实现对字符和数字进行“复制”。结果是 Common Lisp不保证 eq 为真,即使它的两个参数都是“相同的东西”,如果那个东西是一个字符数字

  2. 你可以使用genhash图书馆。首先,为您的类型定义一个新的散列函数(另请参见 参考资料sxhash)和一个测试函数,并将其与一个测试指示符相关联:

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

    然后,您可以定义一个新表:

    (genhash:make-generic-hashtable :test 'category=)
    
于 2015-11-20T14:30:40.237 回答
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
于 2015-11-20T16:20:55.900 回答