Data structures 使用构造键的高效哈希表访问

Data structures 使用构造键的高效哈希表访问,data-structures,common-lisp,hashtable,Data Structures,Common Lisp,Hashtable,公共Lisp允许任何Lisp对象用作哈希表键。但是,如果只想使用对象的一部分作为键,该怎么办。例如,在 (defstruct action (name nil :type symbol) (parameters nil :type list) (time 0 :type fixnum) (database nil :type hash-table)) 该时隙不适用于equalp哈希情况。使用(部分)lisp对象作为键访问哈希表的好策略是什么?一种方法可能使用类似于(list(a

公共Lisp允许任何Lisp对象用作哈希表键。但是,如果只想使用对象的一部分作为键,该怎么办。例如,在

(defstruct action
  (name nil :type symbol)
  (parameters nil :type list)
  (time 0 :type fixnum)
  (database nil :type hash-table))

该时隙不适用于equalp哈希情况。使用(部分)lisp对象作为键访问哈希表的好策略是什么?一种方法可能使用类似于
(list(action name act1)(action parameters act1)(action database act1))
的键,但这似乎效率很低。另一种方法可能只使用三个适当的插槽创建action defstruct的子结构,并将该子结构用作键,但这似乎只是为了访问哈希表而有点特别。还有其他更好的方法吗?

我将使用中的Common Lisp库,例如

然后转到代码,首先创建如下操作:

CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))
CL-USER> (defstruct action
  (name nil :type symbol)
  (parameters nil :type list)
  (time 0 :type fixnum)
  (database (make-hash-table) :type hash-table))
ACTION
CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 45 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012E708D>)
产生此错误:

The value NIL is not of the expected type HASH-TABLE.
   [Condition of type TYPE-ERROR]
因此,您需要将定义更改为以下内容:

CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))
CL-USER> (defstruct action
  (name nil :type symbol)
  (parameters nil :type list)
  (time 0 :type fixnum)
  (database (make-hash-table) :type hash-table))
ACTION
CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 45 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012E708D>)
创建另一个操作

CL-USER>  (setq action-2 (make-action 
   :parameters '(1 2 3) 
   :time 102))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 102 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012FE58D>)
定义哈希函数:

CL-USER> (defun hash-action (a) (action-time a))
HASH-ACTION
创建你的散列

CL-USER> (define-custom-hash-table-constructor make-action-hash :test equal-actions-by-time :hash-function hash-action)
MAKE-ACTION-HASH
CL-USER> (defparameter *foo-hash* (make-action-hash) "variable for stackoverflow")
*FOO-HASH*
试试看:

CL-USER>  (setf (gethash action-1 *foo-hash*) 1
          (gethash action-2 *foo-hash*) 10)
10

CL-USER>  (gethash action-1 *foo-hash*)
1
T
如果发行版将在本机支持自定义测试/哈希函数的实现中工作,则可以避免使用库,否则可以与自定义哈希表一起使用

在optimus的情况下,您可以按如下方式工作:

CL-USER> (defparameter *foo-hash* (make-hash-table :test 'equal-actions-by-time :hash-function 'hash-action))
*FOO-HASH*
CL-USER>  (setf (gethash action-1 *foo-hash*) 1
          (gethash action-2 *foo-hash*) 10)
10
CL-USER>  (gethash action-1 *foo-hash*)
1
T

我将使用中的公共Lisp库,例如

然后转到代码,首先创建如下操作:

CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))
CL-USER> (defstruct action
  (name nil :type symbol)
  (parameters nil :type list)
  (time 0 :type fixnum)
  (database (make-hash-table) :type hash-table))
ACTION
CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 45 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012E708D>)
产生此错误:

The value NIL is not of the expected type HASH-TABLE.
   [Condition of type TYPE-ERROR]
因此,您需要将定义更改为以下内容:

CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))
CL-USER> (defstruct action
  (name nil :type symbol)
  (parameters nil :type list)
  (time 0 :type fixnum)
  (database (make-hash-table) :type hash-table))
ACTION
CL-USER>  (setq action-1 (make-action 
   :parameters '(1 2 3) 
   :time 45))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 45 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012E708D>)
创建另一个操作

CL-USER>  (setq action-2 (make-action 
   :parameters '(1 2 3) 
   :time 102))

#S(ACTION :NAME NIL :PARAMETERS (1 2 3) :TIME 102 :DATABASE #<HASH-TABLE :TEST EQL size 0/60 #x3020012FE58D>)
定义哈希函数:

CL-USER> (defun hash-action (a) (action-time a))
HASH-ACTION
创建你的散列

CL-USER> (define-custom-hash-table-constructor make-action-hash :test equal-actions-by-time :hash-function hash-action)
MAKE-ACTION-HASH
CL-USER> (defparameter *foo-hash* (make-action-hash) "variable for stackoverflow")
*FOO-HASH*
试试看:

CL-USER>  (setf (gethash action-1 *foo-hash*) 1
          (gethash action-2 *foo-hash*) 10)
10

CL-USER>  (gethash action-1 *foo-hash*)
1
T
如果发行版将在本机支持自定义测试/哈希函数的实现中工作,则可以避免使用库,否则可以与自定义哈希表一起使用

在optimus的情况下,您可以按如下方式工作:

CL-USER> (defparameter *foo-hash* (make-hash-table :test 'equal-actions-by-time :hash-function 'hash-action))
*FOO-HASH*
CL-USER>  (setf (gethash action-1 *foo-hash*) 1
          (gethash action-2 *foo-hash*) 10)
10
CL-USER>  (gethash action-1 *foo-hash*)
1
T

请参阅:。至于效率低下,没有适当的时机你不能这么说。这似乎是一个非常小的分配,可以立即收集垃圾。我认为
(database nil:type hash table)
应该是
(database(make hash table):type hash table)
。否则,在创建
操作时会抛出错误。@coredump:没错,一个快速测试显示,使用结构作为键与插槽列表的时间差可以忽略不计。此外,您推荐信中的实习方法虽然不适用于我的应用程序,但在某些情况下可能有用。另一个想法是暂时将不合适的插槽设置为空值,仅用于哈希表访问,并将整个结构保留为键。@tsikov:谢谢您的修改。对于简单的原型设计,我一直在使用Allegro CL Express,它不会抱怨很多类似的事情。请参阅:。至于效率低下,没有适当的时机你不能这么说。这似乎是一个非常小的分配,可以立即收集垃圾。我认为
(database nil:type hash table)
应该是
(database(make hash table):type hash table)
。否则,在创建
操作时会抛出错误。@coredump:没错,一个快速测试显示,使用结构作为键与插槽列表的时间差可以忽略不计。此外,您推荐信中的实习方法虽然不适用于我的应用程序,但在某些情况下可能有用。另一个想法是暂时将不合适的插槽设置为空值,仅用于哈希表访问,并将整个结构保留为键。@tsikov:谢谢您的修改。对于简单的原型设计,我一直在使用Allegro CL Express,它不会抱怨很多类似的事情。感谢您对自定义哈希表的见解。但对于我最初的问题,我仍然不清楚定制与标准的优势。对于这个3槽有效键,我假设自定义哈希表测试函数将使用(and(eq(动作名称a1)(动作名称a2))(equal(动作参数a1)(动作参数a2))(equalp(动作数据库a1)(动作数据库a2)),而标准equalp哈希表将只使用(list(动作名称a1)(动作参数a1))(action database a1))作为键。在这种情况下,自定义表是否比标准表更有效?使用哈希表比列表更有效,请查看这里@davypough您可以手动“转换”在将操作实例用作哈希表的键之前,请先将其转换为列表。使用自定义哈希表的一个优点是可读性:您不必对所有哈希表调用提及此转换,但在定义哈希表时只需提及一次。性能优势在于避免了这些临时列表。感谢您对自定义哈希表的深入了解表。但对于我的原始问题,我仍然不清楚自定义与标准的优势。对于3槽有效键,我假设自定义哈希表测试函数将使用(和(eq(操作名称a1)(操作名称a2))(相等(操作参数a1)(操作参数a2))(equalp(操作数据库a1)(操作数据库a2))),而标准equalp哈希表只使用(list(action name a1)(action parameters a1)(action database a1))作为键。在这种情况下,自定义表是否比标准表更有效?使用哈希表比列表更有效,请查看这里@davypough您可以手动“转换”在将操作实例用作哈希表的键之前,请先将其转换为列表。使用自定义哈希表的一个优点是可读性:您不必对所有哈希表调用提及此转换,但在定义哈希表时只需提及一次。性能优势在于避免了这些临时列表。