In Common Lisp, using lists for most data structures is a mark of a poor programmer. If you think Lisp is about list processing, you perhaps need to become familiar with how Lisp has evolved since the 1970s.
In Common Lisp, using lists for most data structures is a mark of a poor programmer. If you think Lisp is about list processing, you perhaps need to become familiar with how Lisp has evolved since the 1970s.
Recall that the original claim was that software engineering hasn't caught up to LISP. Making metaprogramming trivial by using an identical presentation for code and data is the key insight of LISP, and we do a lot of metaprogramming in many languages, so yes, we've caught up with LISP.
``` (setq a (make-hash-table)) (setf (gethash 'color a) 'brown) (setf (gethash 'name a) 'fred) (gethash 'color a) => brown (gethash 'name a) => fred (gethash 'pointy a) => nil ```
vs
``` a = { color:'brown', name:'fred' } ```
(defparameter a '((color brown)
(name fred)))
(defun make-hashmap (pairs)
(let ((ht (make-hash-table)))
(mapc (lambda (pair)
(setf (gethash (car pair) ht) (cadr pair)))
pairs)
ht))
(defun showhash (hash-table)
"Show hash-table"
(maphash #'(lambda (key value)
(format t "~%~S : ~S" key value))
hash-table))
(showhash (make-hashmap a))(Yes, you can carefully bind
*readtable*
before compiling code, but methods for doing so are ad hoc and not standardized. This is a perfect example of a convention that Common Lisp programmers need to establish formally when using CL for software engineering.)You declare a readtable like this: https://github.com/fiddlerwoaroof/objc-lisp-bridge/blob/mast...
And use it like this: https://github.com/fiddlerwoaroof/objc-lisp-bridge/blob/mast...
{:color “brown” :name “fred”} CL-USER> (set-macro-character #\{
(lambda (str char)
(declare (ignore char))
(let ((*readtable* (copy-readtable *readtable* nil))
(keep-going t))
(set-macro-character #\} (lambda (stream char)
(declare (ignore char) (ignore stream))
(setf keep-going nil)))
(let ((pairs (loop for key = (read str nil nil t)
while keep-going
for value = (read str nil nil t)
collect (list key value)))
(retn (gensym)))
`(let ((,retn (make-hash-table :test #'equal)))
,@(mapcar
(lambda (pair)
`(setf (gethash ,(car pair) ,retn) ,(cadr pair)))
pairs)
,retn)))))
T
CL-USER> {:foo 100500 :bar 42}
#<HASH-TABLE :TEST EQUAL :COUNT 2 {1005879463}>
CL-USER> (alexandria:hash-table-alist #v1)
((:BAR . 42) (:FOO . 100500))
CL-USER>
And define a printer for the hash to output it as desired: CL-USER> (defmethod print-object ((obj hash-table) stream)
(princ "{" stream)
(loop with indent = nil
for key being the hash-keys in obj using (hash-value value)
when indent
do (format stream "~% ")
unless indent
do (setf indent t)
do (format stream "~S ~S" key value))
(princ "}" stream))
#<STANDARD-METHOD COMMON-LISP:PRINT-OBJECT (HASH-TABLE T) {10040C9F83}>
CL-USER> {:foo 100500 :bar 42}
{:FOO 100500
:BAR 42} (defparameter a '((color brown)
(name fred)))But you could do:
(def foo {:color "brown" :name "fred"})
Anytime you want to get the color out you can just call the :color function and pass the map (:color foo)
=> "brown"
(:name foo)
=> "fred"
Likewise, we can call the map as the function and pass the key as the argument (foo :name)
=> "fred"
We also have nice ways to destructure and bind values in maps when passed as parameters to functions (defn my-func [{:keys [color name]}]
(println "Color: " color)
(println "Name: " name))
(my-func foo)
=>
Color: brown
Name: fred>vs
>a = { color:'brown', name:'fred' }
For such simple data you don't need to use a hashtable in Lisp. You just do:
(:color "brown" :name "fred")
then you get the name using:
(getf x :name)
Btw, anything you can represent in JSON, you can also represent in Lisp s-expressions. But not the other way around(!)
Hashtables, in Common Lisp, are intended for big amounts of data, that's why CL includes several options to tune their performance.
This works, but it's a plist and thus O[n] for lookup. I assumed the questioner wanted an O[1] data structure; thus a hash table is the better answer. (Granted it makes little difference when n is small.)
There was a test on some Common Lisp implementations, of hashtables versus plists on access speeds.
On small (<30?) datasets, plists were much faster than hashtables. Bigger than that, the constant access speed of hashtables makes HTs a more sane choice.
The other thing is memory consumption -- on CL, plists are very frugal.