Search code examples
initializationconstantshashtablecommon-lisp

Common Lisp: shorthand to initialize a hash table with many entries


I'm looking for a possibly non-verbose portable way to initialize a hash table in Common Lisp. E.g. something that works for constant hash tables, but also to pre-load variable hashes. In CLISP I am using:

(defconstant +my-map+ #S(HASH-TABLE :TEST FASTHASH-EQ
  (key1 . "value1")
  ...
  (keyN . "valueN")
))

but unfortunately this format only works on CLISP.


Solution

  • One can programmatically construct a hash table at read time:

    (defvar *ht* #.(let ((ht (make-hash-table)))
                     (loop for (key . value) in
                           '((a . 1) (b . 2) (c . 3))
                           do (setf (gethash key ht) value))
                     ht))
    
    (describe *ht*)
    

    #. is used for read time evaluation. The compiler then will dump the hash table to the FASL file.

    This can then be compiled:

    Using SBCL:

    * (compile-file "/tmp/test.lisp")
    
    ; compiling file "/private/tmp/test.lisp" (written 24 MAY 2012 10:08:49 PM):
    ; compiling (DEFVAR *HT* ...)
    ; compiling (DESCRIBE *HT*)
    
    ; /tmp/test.fasl written
    ; compilation finished in 0:00:00.360
    #P"/private/tmp/test.fasl"
    NIL
    NIL
    * (load *)
    
    #<HASH-TABLE :TEST EQL :COUNT 3 {100299EA43}>
      [hash-table]
    
    Occupancy: 0.2
    Rehash-threshold: 1.0
    Rehash-size: 1.5
    Size: 16
    Synchronized: no
    T
    * *ht*
    
    #<HASH-TABLE :TEST EQL :COUNT 3 {100299EA43}>
    

    Creating a hash table as a function:

    (defun create-hashtable (alist
                             &key (test 'eql)
                             &aux (ht (make-hash-table :test test)))
      (loop for (key . value) in alist
            do (setf (gethash key ht) value))
      ht)