Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Common Lisp: getting all keys of a given hash table as a list

Tags:

I wonder if there is a less verbose way than using a loop. Anyway this works for me in CLISP:

(loop for key being the hash-keys of *my-hash* collect key) 

I've seen others using maphash, but that involves accumulating each key into a list. Apart from that being more involved than using loop, it also introduces a side-effect, which I try to avoid whenever I can - I prefer functional programming as much as possible :)

Is there anything predefined like this for this common task, even though implementation-specific?

(defun hash-keys (hash-table)   (loop for key being the hash-keys of hash-table collect key)) 
like image 215
Antonio Bonifati 'Farmboy' Avatar asked Mar 15 '12 12:03

Antonio Bonifati 'Farmboy'


2 Answers

Common Lisp comes from before "batteries-included" philosophy became prevalent, and most functionality is expected to be provided by third-party libraries and not implementations. While Common Lisp is sometimes called a large language, it is only compared to C and similar, the language itself is quite tiny compared to Python and other languages with massive standard libraries.

For this specific purpose, Alexandria is a commonly used collection of Common Lisp utilities. Among many other things it contains hash-table-keys.

like image 191
Ramarren Avatar answered Oct 18 '22 05:10

Ramarren


There is no disadvantage in defining

(defun hash-keys (hash-table)   (loop for key being the hash-keys of hash-table collect key)) 

because in Common Lisp the function is compiled. If your vendor provided this function, it would pretty much do the same thing and not be much more efficient than yours, if at all.

In interpreted languages, nearly anything you write yourself has a performance disadvantage compared to an "intrinsic" routine.

Consing up the contents of a hash is wasteful; loop lets you process the hash without consing up memory. So maybe you want a macro instead (Some Lisps provide a dohash or similar as an extension).

(defmacro do-hash ((key-var val-var hash-expr &optional result-form) &body body)   (let ((hash-var (gensym "HASH-")))      `(loop with ,hash-var = ,hash-expr             for ,key-var being the hash-keys of ,hash-var             for ,val-var being the hash-values of ,hash-var             do (progn ,@body)             finally (return ,result-form)))) 

Or a hash mapping function:

 (defun mapc-hash (hash-table fun)     (loop for key being the hash-keys of hash-table          for value being the hash-values of hash-table          do (funcall fun key value))) 

Should the language have every possible gadget like this that anyone can write in a minute?

In Common Lisp, there are included batteries, but they are other kinds of batteries: things that are actually hard to do. For example, a compile function for dynamically compiling code at run time. It would be prohibitively difficult for most users to develop such a thing from scratch compared to pulling keys or values from a hash table in half a dozen different ways.

like image 27
Kaz Avatar answered Oct 18 '22 03:10

Kaz