Path: utzoo!attcan!uunet!dino!sharkey!mailrus!hellgate.utah.edu!cdr.utah.edu!moore From: moore%cdr.utah.edu@cs.utah.edu (Tim Moore) Newsgroups: comp.lang.lisp Subject: Re: code Message-ID: <1989Nov18.152356.7962@hellgate.utah.edu> Date: 18 Nov 89 22:23:56 GMT References: <464@cs.columbia.edu> Organization: University of Utah CS Dept Lines: 75 In article <464@cs.columbia.edu> mkamer@cs.columbia.edu (Matthew Kamerman) writes: > Here are two Macros I've found useful. PLet and PLet* support persisting >variable values in the abscence of CLOS. The implementation causes some >subtle features/bugs if a function containing a PLet is called recursively. >The documentation goes into this in greater detail. >(DefMacro PLet ((&Rest vars-and-bindings) &Body body) > "(PLet (&Rest vars-and-bindings) &Body body) > [documentation omitted]" > > (LET (vars syms declarations) > (SetQ vars (MapCan #'(LAMBDA (var) > (LIST (IF (ListP var) (FIRST var) var))) > vars-and-bindings) > syms (MapCan #'(LAMBDA (binding &Aux (sym (GenSym))) > (SetF (Symbol-Value sym) binding) > (LIST sym)) > (EVAL `(LET ,vars-and-bindings (LIST ,@vars))))) > (DO ((sexp (FIRST body) (FIRST body))) > ((NOT (AND body (ListP sexp) (EQL (FIRST sexp) 'DECLARE))) > (SetQ declarations (NReverse declarations))) > (PUSH (POP body) declarations)) > (IF vars > `(LET ,(MapCar #'LIST vars syms) > ,@declarations > (UnWind-Protect > ,(AND body `(ProgN ,@body)) > (SetQ ,@(MapCan #'LIST syms vars)))) > `(LET () ,@declarations ,@body)))) > This macro has several serious problems. First off, it won't work interpretively if the interpreter expands the PLet macro more than once, rather than just when the surrounding function is defun'ed. I discovered this by typing your fibonacci example into Utah Common Lisp. You plausibly could say that this is a dumb way to write an interpreter, but it is a valid way, and you shouldn't write macros that depend on being expanded only once. Secondly, the initforms for the PLet bindings can reference only constants or special variables. If the code is being compiled, any special variables must exist in the compiler's environment. This is pretty limiting. Thirdly, the gensyms that hold the values of PLet variables between evaluations exist only in the environment of the compiler. If the compiled code is loaded into a fresh environment, the gensyms will be unbound. If you want persistant lexical bindings you probably should be using closures instead. Here's you fibonacci example, rewritten to use a closure: (defun fib-setup (&key ((:n-2 pn-2) 0) ((:n-1 pn-1) 1)) #'(lambda (&key n-2 n-1) (and n-2 (setq pn-2 n2)) (and n-1 (setq pn-1 n1)) (psetq pn-2 pn-1 pn-1 (+ pn-2 pn-1)) pn-1)) FIB-SETUP (setf (symbol-function 'fibonacci) (fib-setup)) # (fibonacci) 1 (fibonacci) 2 (fibonacci) 3 (fibonacci) 5 Note: I too think it would be a good thing if people posted more code and pointers to code. Tim Moore moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore "Ah, youth. Ah, statute of limitations." -John Waters