-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 =pod I have seen a few complaints about Common Lisp over on Programming Reddit this week. Most of them are the usual kind of complaints you hear from people that are new to any programming language ("I don't understand the common idioms, so the language is bad"), but one complaint keeps coming up: Lisp doesn't support hashes well. There are plenty of things wrong with Lisp, but I don't get this one. So, let's take a look at how we interact with various data structures in Lisp. The basic structure is a linked list. You make one with the C keyword: lang:Common Lisp (defvar *list* (list 1 2 3)) *list* ;; (1 2 3) Then, you can interact with it. You can get the head: (car *list*) ;; 1 You can get the tail: (cdr *list*) ;; (2 3) You can reference arbitrary elements (as with a vector): (elt *list* 2) ;; 3 You can even destructively change arbitrary elements: (setf (elt *list* 2) "OH HAI") *list* ;; (1 2 "OH HAI") Or even make an infinite list: (setf (cdr *list*) *list*) *list* ;; (1 1 1 1 1 1 ... The key here is the pattern. Reads are C<(some-function *list*)>, while writes are C<(setf (some-function *list*) new-value)>. Let's look at arrays next. You make one with C: (defvar *array* (make-array 10)) *array* ;; #(0 0 0 0 0 0 0 0 0 0) The usual array operations apply. You can read an element: (elt *array* 2) ;; 0 And you can set an element: (setf (elt *array* 2) 42) *array* ;; #(0 0 42 0 0 0 0 0 0 0) Notice the pattern -- a function to read, and a C to write. Pretty consistent. Since arrays and lists are both I, we can even use the same function to operate on both. (There is also C and C, which only work on lists and arrays, respectively.) So now we can get to the topic of this article -- hash tables. Let's make one with C: (defvar *hash* (make-hash-table :test 'equal)) *hash* ;; # Note that we are going to use strings as keys, so we use the C test instead of the default C. (Incidentally, equality in Lisp is another thing people whine about, but equality is actually a very complex concept and is really hard to get right in a language that distinguishes references from values. Haskell doesn't, and it gets equality right. Other languages (with references) try guess what you want to varying effect. Perl usually gets it right, PHP nearly always gets it wrong and introduces subtle data-dependent bugs into your program. Lisp doesn't even try to guess what you want, it just gives you a handful of confusingly-named functions for every kind of equality that you could possibly want, then it lets you decide which to use. Annoying if you are learning the language, but less annoying than languages that try and fail to read your mind, and really not-that-annoying once you get used to it. My fix for this problem is a topic for another day, but it's really not so bad.) OK, so at this point C<*hash*> is empty (C<:COUNT 0>), so it's kind of silly to read it, but we will do it anyway. Let's look up the "foo" key, which should be C right now: (gethash "foo" *hash*) ;; NIL, NIL C actually returns two values (via C); the first is the value of that hash position, the second is a boolean representing whether or not the key is even in the table. (Compare this to C<< $hash->{foo} >> and C<< exists $hash->{foo} >> in Perl.) Now let's add something to the "foo" key: (setf (gethash "foo" *hash*) "OH HAI") *hash* ;; # Then we can get the value for "foo": (gethash "foo" *hash*) ;; "OH HAI", T So we got "OH HAI" back, and we know that that is a value we explicitly set because the second return value is true. That's really all there is to say. All of Lisp's data structures work in essentially the same way. There is some constructor, usually C. Then you have a function to read values, and you use C on that same function to write values. This is no different than languages like Perl, where you use special syntax to read values, and you put that special syntax to the left of an C<=> sign to write values. In conclusion, I really don't understand what the problem is here. Update: The L brought up some good points, and is worth a read. This is the most intelligent discussion I've read on Reddit in the last year. I guess the right people were awake when I submitted the article! One of the issues that was brought up is how collections as a whole are handled (as opposed to single elements, as above). Generally, C is how you deal with collections, but it is really non-Lisp-like, and it has horrible syntax for hash tables. I really recommend installing L. Then we can treat collections almost generically: (iter (for elt in-sequence array) ...) (iter (for elt in-sequence list) ...) (iter (for (k v) in-hashtable hash) ...) (The C<...> can be what you would usually put in a LOOP clause, like C, or C, or C, or arbitrary conditionals and code. The good news is that "keywords" like COLLECT can be nested arbitrarily deep, and you don't have to use a mini-language.) This leads to expressive code for working with hash tables, like: (let ((out-of-stock (iter (for (nickname product) in-hashtable (products-in *shopping-cart*)) (when (out-of-stock-p product) (collect nickname))))) (when out-of-stock (format t "Sorry, some items are not in stock: ~A~%" out-of-stock))) -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.9 (GNU/Linux) iEYEARECAAYFAkl9TRkACgkQ2rw+dVvzZm1VIgCfVfDjcZrmOeqKZgFVh5/IifJy CUgAnA1iTWs+2I4ScJZg5Su/o0d8g1Sa =KYUS -----END PGP SIGNATURE-----