[Small-cl-src-discuss] defun-with-cache (take 2)
Kristian Elof Sørensen
elof at image.dk
Mon Dec 12 04:42:37 UTC 2005
Hi
lør, 10 12 2005 kl. 22:19 -0500, skrev Ben Hyde:
> Take another wack at defun-with-cache. This version should handle
> the optional arg provided variables bound in some lambda lists. I
> still assume that rip-apart-lambda-list has bugs. My thanks to
> Kristian Elof Sørensen for doing some actual testing. I was amused
> to discover that *keywork-package* and *keyword-package* were defined
> in the the larger program this is a little part of. :-) - ben
This version passed all the tests from my previous bug report. It is
very nice to see such a fast turn around time for bug fixes.
There was a single compile error this time around:
(cond
((consp arg)
(accumulate (first arg))
(when (third arg)
(accumulate (third arg)))) ;; It said (third-arg) here ???
Compiling this macro with sbcl gives a "Style Warning" saying that
"call" is never referenced. If you have no use for "call" then you could
swap the (multiple-value-bind (binding call) ... for a (let
((binding ... :
(defmacro defun-with-cache (name args &body body)
"Like defun, but this memoizes the function into a cache that maybe
latter cleared."
(multiple-value-bind (binding call)
(rip-apart-lambda-list args)
>
> ;;; -*- Lisp -*- mode
>
> (cl:defpackage "DEFUN-WITH-CACHE"
> (:use "COMMON-LISP")
> (:export "DEFUN-WITH-CACHE" "CLEAR-CACHE-OF-FUNCTION"))
>
> (in-package "DEFUN-WITH-CACHE")
>
> (defun rip-apart-lambda-list (lambda-list)
> "Given a lambda list returns three values. The list of symbols
> bound it binds. Using those, a call argument list for invoking
> the function passing all arguements (see notes). And finally a
> flag indicating if the last arguement is a &rest, i.e. if you
> need to use apply rather than funcall on that arglist. Note
> this does note optional arguement flags, but the arglist returned
> assumes all arguements are passed."
> (let ((binds nil)
> (call nil)
> (apply? nil))
> (flet ((wrapup ()
> (return-from
> rip-apart-lambda-list
> (values
> (nreverse binds)
> (if apply?
> (nreverse (cons apply? call))
> (nreverse call))
> apply?))))
> (loop
> with keys? = nil
> finally (wrapup)
> for arg in lambda-list
> do
> (flet ((accumulate (var)
> (push var binds)
> (when (eq t apply?)
> (setf apply? var)
> (return-from accumulate))
> (when keys?
> (push (intern (symbol-name var) #.(symbol-package :a))
> call))
> (push var call)))
> (cond
> ((consp arg)
> (accumulate (first arg))
> (when (third arg)
> (accumulate (third-arg))))
> ((eq arg '&aux)
> (wrapup))
> ((eq arg '&rest)
> (setf apply? t))
> ((eq arg '&optional)
> (setf apply? t))
> ((eq arg '&allow-other-keys)
> (setf apply? t))
> ((eq arg '&key)
> (setf keys? t))
> ((symbolp arg)
> (accumulate arg))))))))
>
> (defmacro cache-of-function (function-name)
> "An equal hash table maybe stored on the plist of a function for
> caching."
> `(get ,function-name :cache-of-function))
>
> (defun clear-cache-of-function (function-name)
> "Forget any cached results from invoking function of the given
> symbol."
> (clrhash (cache-of-function function-name)))
>
> (defmacro defun-with-cache (name args &body body)
> "Like defun, but this memoizes the function into a cache that maybe
> latter cleared."
> (multiple-value-bind (binding call)
> (rip-apart-lambda-list args)
> `(let ((#1=#:cache (make-hash-table :test #'equal)))
> (setf (cache-of-function ',name) #1#)
> (defun ,name ,args
> (let ((#2=#:key (list , at binding)))
> (values-list
> (or (gethash #2# #1#)
> (setf (gethash #2# #1#)
> (multiple-value-list
> (progn
> , at body))))))))))
>
>
>
>
> _______________________________________________
> Small-cl-src-discuss mailing list
> Small-cl-src-discuss at hexapodia.net
> http://www.hexapodia.net/mailman/listinfo/small-cl-src-discuss
More information about the Small-cl-src-discuss
mailing list