I was writing some code this morning and wanted to create a hash table
with weak keys*. This isn't part of the Common Lisp standard, but most
vendors support it. Unfortunately, vendors have different syntax for
this.
It would be nice to have a cross-implementation way of doing this, for
example:
make-weak-hash-table &key weak test size rehash-size rehash-threshold
=> hash-table
test, size, rehash-size, and rehash-threshold would be as for
MAKE-HASH-TABLE.
WEAK would be one of:
NIL -- no weak keys
:KEYS -- An entry is key if there are pointers to the key
:VALUES -- An entry is key if there are pointers to the value
:BOTH -- An entry is kept if there are pointers to both the key and
the value.
:EITHER -- An entry is kept if there are pointers to either the key
and the value.
(This is, more or less, the syntax from Lispworks, according to the
online documentations).
Here are my first attempts at writing this. Other implementations are
requested.
(defvar *default-ht-size*
(hash-table-size (make-hash-table)))
(defvar *default-ht-rehash-size*
(hash-table-rehash-size (make-hash-table)))
(defvar *default-ht-rehash-threshold*
(hash-table-rehash-threshold (make-hash-table)))
#+allegro
(defun make-weak-hash-table
(&key weak
(test #'eql)
(size *default-ht-size*)
(rehash-size *default-ht-rehash-size*)
(rehash-threshold *default-ht-rehash-threshold*))
(when (eq weak :either)
(setq weak :both)
(cerror "Use :BOTH instead."
"Allegro does not support :EITHER-type weak hash tables."))
(ecase weak
((NIL)
(make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold))
((:keys)
(make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold
:weak-keys t))
((:values)
(make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold
:values :weak))
((:both)
(make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold
:weak-keys t
:values :weak))))
#+lispworks
(defun make-weak-hash-table
(&key weak
(test #'eql)
(size *default-ht-size*)
(rehash-size *default-ht-rehash-size*)
(rehash-threshold *default-ht-rehash-threshold))
(ecase weak
((:values keys :both :either)
(let ((ht (make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold
:weak-keys t)))
(set-hash-table-weak ht weak)
ht))))
#-(or allegro lispworks)
(defun make-weak-hash-table
(&key weak
(test #'eql)
(size *default-ht-size*)
(rehash-size *default-ht-rehash-size*)
(rehash-threshold *default-ht-rehash-threshold))
(declare (ignore weak))
(cerror "Make 'non-weak' hash table instead."
"This implementation doesn't (yet) support weak hash-tables")
(make-hash-table :test test
:size size
:rehash-size rehash-size
:rehash-threshold rehash-threshold))
* More or less, for a hash table with weak keys means that a key/value
entry in the hash table will be garbage-collected if the only
'pointer' to the key is the entry in the hash table.
··········@inetmi.com (Will Fitzgerald) writes:
> #+allegro
> (defun make-weak-hash-table
> (&key weak
> (test #'eql)
> (size *default-ht-size*)
> (rehash-size *default-ht-rehash-size*)
> (rehash-threshold *default-ht-rehash-threshold*))
> ...
and then
> #+lispworks
> (defun make-weak-hash-table
> (&key weak
> (test #'eql)
> (size *default-ht-size*)
> (rehash-size *default-ht-rehash-size*)
> (rehash-threshold *default-ht-rehash-threshold))
> ...
etc. etc.
What you want is to put the #+ stuff inside the defun, so that you don't
have so many repeated function headers.
dave
"Dave Bakhash" <·····@alum.mit.edu> wrote in message
····················@no-knife.mit.edu...
> ··········@inetmi.com (Will Fitzgerald) writes:
>
> > #+allegro
> > (defun make-weak-hash-table
> > (&key weak
> > (test #'eql)
> > (size *default-ht-size*)
> > (rehash-size *default-ht-rehash-size*)
> > (rehash-threshold *default-ht-rehash-threshold*))
> > ...
>
> and then
>
> > #+lispworks
> > (defun make-weak-hash-table
> > (&key weak
> > (test #'eql)
> > (size *default-ht-size*)
> > (rehash-size *default-ht-rehash-size*)
> > (rehash-threshold *default-ht-rehash-threshold))
> > ...
>
> etc. etc.
>
> What you want is to put the #+ stuff inside the defun, so that you don't
> have so many repeated function headers.
>
> dave
Mostly I put them where they were so it is abundantly clear to readers of
c.l.l that what I was looking for are different implementations.
But apparently, I didn't do a very good job as no one has found it a useful
thing to reply to.
- Will Fitzgerald
"Will Fitzgerald" <·················@ameritech.net.no.spam.please> writes:
> But apparently, I didn't do a very good job as no one has found it a useful
> thing to reply to.
Well, here's my $0.02 ... I don't think it's a good interface. It's
just renaming the Lispworks MAKE-HASH-TABLE function, without
considering what it would take to make this work in other
implementations. CMUCL and CLISP, for example, both have weak hash
tables, but only weak keys. So to emulate weak entries, you'll need
to use their weak pointer facilities. Which means you'll need to wrap
calls to GETHASH and (SETF GETHASH), and the entry-weak hash tables
can't be passed to code that doesn't know about them. A complete
interface would need to be more like:
WEAK-HASH:MAKE-HASH-TABLE (function)
WEAK-HASH:STANDARD-HASH-TABLE (class)
WEAK-HASH:WEAK-HASH-TABLE (class)
WEAK-HASH:GETHASH (generic function)
(SETF WEAK-HASH:GETHASH) (generic function)
--
/|_ .-----------------------.
,' .\ / | No to Imperialist war |
,--' _,' | Wage class war! |
/ / `-----------------------'
( -. |
| ) |
(`-. '--.)
`. )----'