[Small-cl-src-discuss] Re: [Small-cl-src] defun-with-cache

Ben Hyde bhyde at pobox.com
Sat Dec 10 06:25:56 UTC 2005


On Dec 10, 2005, at 1:01 AM, Kristian Elof Sørensen wrote:

> Hi
>
> I played around with your code a bit.
>
> There are two compilation errors on sbcl, cmucl and clisp on linux.
>
>  1) (finish) does not exist - used when the user supplies  &aux

right, the intent was to bail from the loop; but i need to reframe the
finally

>  2) *keywork-package* does not exist

yeah.

> I changed (finish) into a call to warn
> I chanhed *keyword-package* into "KEYWORD" which intern the symbol  
> as a
> keyword
>
> With these two changes, it seems that ordinary arguments works but
> optional does not.
>
> Keyword arguments mostly work but there is a problem with the "was  
> this
> keyword parameters value set it is it its default value" parameter.

hm, that is difficult to fix.  I may need to loose the flet.

> CL-USER> (defun-cache:defun-with-cache foo (bar baz &optional op0 op1)
> 	   (list :bar bar :baz baz :op0 op0 :op1 op1))
> FOO
>
> CL-USER> (defun-cache:defun-with-cache foo1 (bar baz &key op0 (op1 42
> op1-set))
> 	   (list :bar bar :baz baz :op0 op0 :op1 op1 :op1-set op1-set))
> ;
> ; caught STYLE-WARNING:
> ;   The variable OP1-SET is defined but never used.
> ;
> ; compilation unit finished
> ;   caught 1 STYLE-WARNING condition
> FOO1
>
> CL-USER> (loop for expr in '((foo 1 2 3 4) (foo 4 3 2) (foo 4 3 '(2)
> '(1)) (foo 1 2) (foo 1)
> 			     (foo1 1 2 :op1 42) (foo1 1 2 :op0 42) (foo1 1 2 :op1 42 :op0
> 24))
>
> 	       do (format t "~A  " expr)
> 	       do (catch 'trap-errors
> 		    (handler-bind ((type-error (lambda (err)
> 						 (format t "caught type-error: ~A~%" err)
> 						 (throw 'trap-errors nil)))
> 				   (error (lambda (err)
> 						 (format t "caught error: ~A~%" err)
> 						 (throw 'trap-errors nil))))
> 		      (format t "~A no error thrown~%" (eval expr)))))
> (FOO 1 2 3 4)  caught type-error: The value 3 is not of type LIST.
> (FOO 4 3 2)  caught type-error: The value 2 is not of type LIST.
> (FOO 4 3 '(2) '(1))  (BAR 4 BAZ 3 OP0 (1) OP1 2) no error thrown
> (FOO 1 2)  (BAR 1 BAZ 2 OP0 NIL OP1 NIL) no error thrown
> (FOO 1)  caught error: invalid number of arguments: 1
> (FOO1 1 2 OP1 42)  (BAR 1 BAZ 2 OP0 NIL OP1 42 OP1-SET T) no error
> thrown
> (FOO1 1 2 OP0 42)  (BAR 1 BAZ 2 OP0 42 OP1 42 OP1-SET T) no error  
> thrown
> (FOO1 1 2 OP1 42 OP0 24)  (BAR 1 BAZ 2 OP0 24 OP1 42 OP1-SET T) no  
> error
> thrown
> NIL
>
> The three first expressions shows some problems with optional  
> arguments
>
> In the second to last expression the result for (FOO1 1 2 OP0 42) is
> returning op1-set as t where it should be nil

nice.

thanks, ben.

> 	Kristian
>
>
> fre, 09 12 2005 kl. 22:38 -0500, skrev Ben Hyde:
>> This is a lark.
>>
>> Given a function:    (defun f (...) ...)
>>
>> You can rewrite that into:   (defun-with-cache f (...) ...)
>>
>> at which point a second call upon f with the same arguments will
>> return the values returned the first time.  You can clear the cache
>> by calling.
>>    (clear-cache-of-function 'f)
>>
>> If I was to guess where this is likely to have a bug I'd pick rip-
>> apart-arglist; who's job is to handle &optional, &rest, &keys, etc.
>> If I was to pick the part likely to make the casual reader's brain
>> hurt it would be body of defun-with-cache, which was pure fun to  
>> write.
>>
>> It's fine if F returns multiple values.
>>
>> This does what I need in the code where I'm using it, so "I think I'm
>> happy."
>>
>>    - ben
>>
>>
>> (defun rip-apart-arglist (arglist)
>>    (loop
>>        with binds = nil
>>        with call = nil
>>        with apply? = nil
>>        with keys? = nil
>>        finally (return (values
>> 		       (nreverse binds)
>> 		       (if apply?
>> 			   (nreverse (cons apply? call))
>> 			   (nreverse call))
>> 		       apply?))
>>        for arg in arglist
>>        do
>>        (flet ((accumulate (var)
>> 	       (push var binds)
>> 	       (when (eq t apply?)
>> 		 (setf apply? var)
>> 		 (return-from accumulate))
>> 	       (when keys?
>> 		 (push (intern (symbol-name var) *keywork-package*)
>> 		       call))
>> 	       (push var call)))
>> 	(cond
>> 	  ((consp arg)
>> 	   (accumulate (first arg)))
>> 	  ((eq arg '&aux)
>> 	   (finish))	
>> 	  ((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)
>>    `(get ,function-name :cache-of-function))
>>
>> (defun clear-cache-of-function (function-name)
>>    (clrhash (cache-of-function function-name)))
>>
>> (defmacro defun-with-cache (name args &body body)
>>    (multiple-value-bind (binding call apply?)
>>        (rip-apart-arglist args)
>>      `(let ((#1=#:cache (make-hash-table :test #'equal)))
>>        (setf (cache-of-function ',name) #1#)
>>        (defun ,name ,args
>> 	(flet ((,name ,args , at body))
>> 	  (let ((#2=#:key (list , at binding)))
>> 	    (values-list
>> 	     (or (gethash #2# #1#)
>> 		 (setf (gethash #2# #1#)
>> 		       (multiple-value-list
>> 			   ,@(if apply?
>> 				 `((apply #',name , at call))
>> 				 `((,name , at call)))))))))))))
>>
>> _______________________________________________
>> Small-cl-src mailing list
>> Small-cl-src at hexapodia.net
>> http://www.hexapodia.net/mailman/listinfo/small-cl-src
>
>
> _______________________________________________
> 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