散列表,也就是hash-table,在编程过程中会经常使用到,它以“关键字--数值”这样的对应关系保存数据,适合用于保存那些需要通过关键字查找数值的数据。
这里不打算详细介绍散列表的原理,希望深入了解散列表的同学请参考其它相关资料,这里只是介绍如何在Lisp中使用散列表。
再说,这种涉及理论的高深话题也不是我这种半途出家的人能说明白的,就是叫我讲我也不一定讲的清楚。说来惭愧,在刚开始工作的第一年中,我几乎是在完全无知的状态下异常开心地使用着散列表,直到有一天抓住一个实习的计算机研究生,以“不耻上问”的态度死缠烂打才问明白。
在Lisp中创建散列表很简单,简单到不需要参数,直接调用make-hash-table就可以了,如以下样例:
(setf my-hash-t (make-hash-table))
以上代码通过(make-hash-table)创建了一个散列表,再将它赋予变量my-hash-t。
如果需要获取散列表中某一个关键字对应的数值,可以使用gethash函数,如以下代码可以获取散列表my-hash-t中关键字NB001对应的数值:
(gethash 'NB001 my-hash-t)
当然,如果只是创建了散列表,并没有添加元素,以上代码会返回NIL的。
为了给散列表添加元素,可以直接使用setf函数,第一个参数是gethash获得的关键字位置,第二个参数是希望添加的数值,如:
(setf (gethash 'NB002 my-hash-t) "Tom")
以上代码为散列表my-hash-t添加了一对元素,其关键字为NB002,数值为“Tom”
需要注意的是散列表中一个关键字只能对应一个数值,同一个关键字无法赋予两个不同的值。如下面的代码会将NB001的数值设置为“Jerry”。后面那句setf生效,这个和变量的赋值的类似的。
(setf (gethash 'NB001 my-hash-t) "Mike")
(setf (gethash 'NB001 my-hash-t) "Jerry")
最后是有关散列表的遍历,Lisp中的散列表可以通过maphash函数进行迭代,形式如下:
(maphash #'(lambda (key value)
(format *query-io* "key: ~a value: ~a ~%" key value))
my-hash-t)
以上代码使用了lambda定义了一个匿名函数,该函数接受key和value两个参数,函数中输出key和value的值。
maphash函数调用的形式是:(maphash 匿名函数 散列表), maphash函数会将散列表中的关键字和对应数值逐对提取出来,赋予“匿名函数”的两个参数,然后调用匿名函数。
有关lambda的使用我们后面再详细讨论,这里简单把它看作是一个匿名函数就可以了。
下面是Lisp中使用散列表的完整样例和对应执行截图:
(defun hash-test ()
(setf my-hash-t (make-hash-table))
(setf (gethash 'NB001 my-hash-t) "Mike")
(setf (gethash 'NB002 my-hash-t) "Tom")
(setf (gethash 'NB003 my-hash-t) "Rose")
(format *query-io* "key: NB001 value: ~a ~%" (gethash 'NB001 my-hash-t))
(setf (gethash 'NB001 my-hash-t) "Jerry")
(format *query-io* "key: NB001 value: ~a ~%" (gethash 'NB001 my-hash-t))
(format *query-io* "going to print all the elements~%")
(maphash #'(lambda (key value)
(format *query-io* "key: ~a value: ~a ~%" key value))
my-hash-t)
)