Adding Values to Hash Table
Working with Hash Tables in Common Lisp
Section titled “Working with Hash Tables in Common Lisp”Hash tables are a fundamental and highly efficient data structure in Common Lisp for storing key-value pairs. They provide near constant-time performance for lookups, insertions, and deletions. This guide covers creating, manipulating, and effectively using hash tables in modern Lisp projects.
Creating a Hash Table
Section titled “Creating a Hash Table”You create a hash table using make-hash-table. A crucial argument is :test, which specifies the function used to compare keys. The choice of :test is critical for correctness.
'eq: Compares if two objects are the same in memory. Use for symbols or objects you know are identical.'eql(The default): Compares objects usingeq, but also works for identical numbers and characters.'equal: Compares if two objects have a similar structure and content. Use this for string keys.'equalp: A more lenient version ofequal, ignoring case in strings and comparing numeric types.
;; A hash table for symbol keys (default :test is 'eql)(defvar *user-ids* (make-hash-table))
;; A hash table for string keys (best practice is :test 'equal)(defvar *user-prefs* (make-hash-table :test 'equal))Adding and Updating Entries
Section titled “Adding and Updating Entries”The standard way to add or update a value in a hash table is by using setf with gethash. If the key already exists, its value is updated; otherwise, a new key-value pair is created.
;; gethash retrieves the value for a key. If the key doesn't exist, it returns nil.;; setf modifies the place, associating the new value with the key.
(setf (gethash 'user-001 *user-ids*) 1024)(setf (gethash "username" *user-prefs*) "alex")(setf (gethash "theme" *user-prefs*) "dark")Retrieving Values
Section titled “Retrieving Values”gethash is used to retrieve a value. It returns two values: the found value (or nil if not found), and a boolean t or nil indicating whether the key was present. Checking the second return value is the only reliable way to distinguish between a stored value of nil and a key that does not exist.
;; Simple retrieval(gethash 'user-001 *user-ids*) ;=> 1024, t
;; Retrieving a non-existent key(gethash 'user-999 *user-ids*) ;=> NIL, NIL
;; Handling the two return values properly(multiple-value-bind (value foundp) (gethash "theme" *user-prefs*) (if foundp (format t "Theme is: ~a~%" value) (format t "No theme set.~%")))Removing Entries
Section titled “Removing Entries”Use remhash to remove a key-value pair from a hash table. It returns t if the key was found and removed, and nil otherwise.
(remhash 'user-001 *user-ids*) ;=> tPractical Example: Word Frequency Counter
Section titled “Practical Example: Word Frequency Counter”Let’s build a function that counts word frequencies in a piece of text, a classic use case for hash tables.
Code: main.lisp
Section titled “Code: main.lisp”(defun word-frequency (text) "Counts the frequency of each word in a text string." (let ((counts (make-hash-table :test 'equalp)) ; 'equalp for case-insensitivity (words (str:words (str:downcase text)))) ; Using the 'str' library (dolist (word words) ;; (incf (gethash word counts 0)) is a concise way to increment ;; the count. If the key doesn't exist, (gethash ...) returns the ;; default value 0, which is then incremented to 1 and stored. (incf (gethash word counts 0))) counts))
;; Example Usage (requires 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))Sample Output
Section titled “Sample Output”QUESTION: 1THE: 1IS: 1THAT: 1BE: 2NOT: 1OR: 1TO: 2Common Errors and Debugging
Section titled “Common Errors and Debugging”- Wrong
:testfunction: The most common error. Using the default'eqlfor string keys will fail because two identical-looking strings are not necessarilyeql. Always use'equalor'equalpfor string keys. - Assuming
gethashreturns only one value: Forgetting to check the second return value (foundp) can lead to bugs when a key’s value is legitimatelynil.