向哈希表添加值
Common Lisp 中的哈希表操作
Section titled “Common Lisp 中的哈希表操作”哈希表是 Common Lisp 中用于存储键值对的基本且高效的数据结构。它们为查找、插入和删除操作提供了接近常数时间(constant-time)的性能。本指南涵盖了在现代 Lisp 项目中创建、操作和有效使用哈希表。
你可以使用 make-hash-table 创建哈希表。一个关键参数是 :test,它指定了用于比较键的函数。:test 的选择对于正确性至关重要。
'eq:比较两个对象在内存中是否相同。用于符号或你确认为相同(identical)的对象。'eql(默认):使用eq比较对象,但也适用于相同的数字和字符。'equal:比较两个对象是否具有相似的结构和内容。用于字符串键。'equalp:equal的更宽松版本,忽略字符串中的大小写并比较数字类型。
;; 用于符号键的哈希表(默认 :test 为 'eql)(defvar *user-ids* (make-hash-table))
;; 用于字符串键的哈希表(最佳实践是 :test 'equal)(defvar *user-prefs* (make-hash-table :test 'equal))添加和更新条目
Section titled “添加和更新条目”在哈希表中添加或更新值的标准方法是使用 setf 配合 gethash。如果键已存在,则其值会被更新;否则,将创建新的键值对。
;; gethash 检索键的值。如果键不存在,则返回 nil。;; setf 修改位置,将新值与键关联。
(setf (gethash 'user-001 *user-ids*) 1024)(setf (gethash "username" *user-prefs*) "alex")(setf (gethash "theme" *user-prefs*) "dark")gethash 用于检索值。它返回两个值:找到的值(如果未找到则为 nil),以及一个布尔值 t 或 nil,指示键是否存在。检查第二个返回值是区分存储值为 nil 和键不存在的唯一可靠方法。
;; 简单检索(gethash 'user-001 *user-ids*) ;=> 1024, t
;; 检索不存在的键(gethash 'user-999 *user-ids*) ;=> NIL, NIL
;; 正确处理两个返回值(multiple-value-bind (value foundp) (gethash "theme" *user-prefs*) (if foundp (format t "Theme is: ~a~%" value) (format t "No theme set.~%")))使用 remhash 从哈希表中移除键值对。如果找到了键并成功移除,则返回 t,否则返回 nil。
(remhash 'user-001 *user-ids*) ;=> t实用示例:单词频率计数器
Section titled “实用示例:单词频率计数器”让我们构建一个函数来计算文本中的单词频率,这是哈希表的一个经典用例。
代码:main.lisp
Section titled “代码:main.lisp”(defun word-frequency (text) "统计文本字符串中每个单词的频率。" (let ((counts (make-hash-table :test 'equalp)) ; 'equalp 用于不区分大小写 (words (str:words (str:downcase text)))) ; 使用 'str' 库 (dolist (word words) ;; (incf (gethash word counts 0)) 是一种简洁的方式来增加计数。 ;; 如果键不存在,(gethash ...) 返回默认值 0,然后将其递增到 1 并存储。 (incf (gethash word counts 0))) counts))
;; 示例用法(需要 ql:quickload "str")(let* ((text "To be or not to be, that is the question.") (freqs (word-frequency text))) (maphash (lambda (key value) (format t "~a: ~a~%" key value)) freqs))QUESTION: 1THE: 1IS: 1THAT: 1BE: 2NOT: 1OR: 1TO: 2常见错误和调试
Section titled “常见错误和调试”- 错误的
:test函数:最常见的错误。对字符串键使用默认的'eql将会失败,因为两个看起来相同的字符串不一定是eql的。始终对字符串键使用'equal或'equalp。 - 假设
gethash只返回一个值:忘记检查第二个返回值 (foundp) 可能会导致当键的值合法地为nil时出现错误。