Path: utzoo!utgpu!jarvis.csri.toronto.edu!clyde.concordia.ca!uunet!cs.utexas.edu!rutgers!aramis.rutgers.edu!atanasoff.rutgers.edu!lou From: lou@atanasoff.rutgers.edu (Lou Steinberg) Newsgroups: comp.lang.lisp Subject: Re: PLet Message-ID: Date: 9 Jan 90 17:12:15 GMT References: <509@cs.columbia.edu> <1515@skye.ed.ac.uk> Organization: Rutgers Univ., New Brunswick, N.J. Lines: 65 To: jeff@aiai.UUCP In article <1515@skye.ed.ac.uk> jeff@aiai.ed.ac.uk (Jeff Dalton) writes: > The problem with the lexically enclosed function [to implement > static variables] is that you have to > wrap something around the whole function definition. [...] > Sometimes you want something more local. For example, you might have > a macro that wants to expand into something that involves a once-only > evaluation. The macro can't wrap a LET around the function it appears > in. > > I`ve sometimes used the following trick > [macro definition deleted] > Unfortunately, both my macro and PLET have a problem: they create a > new symbol each time they're expanded. Since that might be more than > once, they don't really cause the evaluation to happen only once. There is a way to get around this: embed the MACRO definition in a let that creates a hash table mapping macro-calls to their value. The macro expands into something that checks the hash table to see if the form in this call has already been evaluated. If so it just returns that stored value, otherwise it stores the value in the hash table and returns it. Two macro expansions are expanding the "same call" if the macro expressions being expanded are eq. Here is an example: (let ((once-only-cache (make-hash-table))) (defmacro eval-once (&whole whole form) `(multiple-value-bind (value foundp) (gethash ',whole ',once-only-cache) (if foundp value (setf (gethash ',whole ',once-only-cache) ,form))))) For example (using setq / eval to get multiple macro expansions): (setq compute-a '(eval-once (progn (format t "~&Computing a...~%") 10))) (eval compute-a) Computing a... 10 (eval compute-a) 10 Note that we hash on the "whole" rather than the argument ("form") because the argument might be just a symbol, which might also be the argument of some other call to this macro. However, even this approach can be wrong - the "whole" can be eq in two different macro calls if the whole itself comes from a constant in another macro. E.g.: (defmacro foo (x) `(cons (eval-once *x*) ,x)) (let ((*x* 1)) (print (eval-once 3)) (setq *x* 2) (eval-once 3)) 3 3 -- Lou Steinberg uucp: {pretty much any major site}!rutgers!aramis.rutgers.edu!lou arpa: lou@cs.rutgers.edu