(with-fast-alist name form)
causes name
to be a fast alist for the
execution of form
.
Major Section: HONS-AND-MEMOIZATION
Logically, with-fast-alist
just returns form
.
Under the hood, we cause alist
to become a fast alist before executing
form
. If doing so caused us to introduce a new hash table, the hash table
is automatically freed after form
completes.
More accurately, under the hood (with-fast-alist name form)
essentially
expands to something like:
(if (already-fast-alist-p name) form (let ((<temp> (make-fast-alist name))) (prog1 form (fast-alist-free <temp>))))
Practically speaking, with-fast-alist
is frequently a better choice then
just using make-fast-alist
, and is particularly useful for writing
functions that can take either fast or slow alists as arguments. That is,
consider the difference between:
(defun bad (alist ...) (let* ((fast-alist (make-fast-alist alist)) (answer (expensive-computation fast-alist ...))) (prog2$ (fast-alist-free fast-alist) answer))) (defun good (alist ...) (with-fast-alist alist (expensive-computation alist ...)))
Either approach is fine if the caller provides a slow alist
. But if the
input alist
is already fast, bad
will (perhaps unexpectedly) free it!
On the other hand, good
is able to take advantage of an already-fast
argument and will not cause it to be inadvertently freed.
See also the macro with-fast-alists
defined in the community book
"books/centaur/misc/hons-extra.lisp"
, which allows you to call
with-fast-alist
on several alists simultaneously.
The community book "books/centaur/misc/hons-extra.lisp"
extends the
b*
macro (defined in the community book "books/tools/bstar.lisp"
)
with the with-fast
pattern binder. That is, after executing
(include-book "centaur/misc/hons-extra.lisp" :dir :system)
you may write
something like this:
(b* (... ((with-fast a b c ...)) ...) ...)
which causes a
, b
, and c
to become fast alists until the completion
of the b*
form.
Note that with-fast-alist
will cause logically tail-recursive functions not
to execute tail-recursively if its cleanup phase happens after the
tail-recursive call returns.