bevan@cs.man.ac.uk (Stephen J Bevan) (09/21/90)
I've recently been reading a book on Scheme (The Scheme Programming Language - R. Kent Dybvig) and in it, it uses a function `record-case'. This is similar to `case' except that it does destructuring. So for example I could define the following function which given some expressions as lists, evaluates them. (defun eval-expr (x) (record-case x (add (x y) (+ x y)) (sub (x y) (- x y)) (mul (x y) (* x y)) (div (x y) (/ x y)) ) ) >(foo '(add 3 4)) 7 >(foo '(mul 4 5)) 20 This notation is particularly nice if you are writing simple evaluators for languages. Here's the problem. I've tried to implement this in Lisp myself, but to no avail. I've RTFM on macros* but I can't seem to get the arguments to evaluate at the correct time. So I'm hoping some kind soul will show me how this is done. If you are a real masochist I can even send you the code I've written so far (all 53 lines of it!), so you can point out what is wrong with it. Yours a struggling Lisper, Stephen J. Bevan bevan@cs.man.ac.uk * if fact I've got all of the following in front of me Common Lisp, Steele et al. Common Lisp - A Tutorial, Wendy L. Milner Lisp, Winston & Horn but I'm still can't get the parms. to evaluate when I want them to.
moore%cdr.utah.edu@cs.utah.edu (Tim Moore) (09/22/90)
In article <BEVAN.90Sep21154633@panda.cs.man.ac.uk> bevan@cs.man.ac.uk (Stephen J Bevan) writes: >I've recently been reading a book on Scheme (The Scheme Programming >Language - R. Kent Dybvig) and in it, it uses a function >`record-case'. This is similar to `case' except that it does >destructuring. So for example I could define the following function >which given some expressions as lists, evaluates them. > >(defun eval-expr (x) > (record-case x > (add (x y) (+ x y)) > (sub (x y) (- x y)) > (mul (x y) (* x y)) > (div (x y) (/ x y)) > ) > ) > >>(foo '(add 3 4)) >7 >>(foo '(mul 4 5)) >20 ... >Here's the problem. I've tried to implement this in Lisp myself, but >to no avail. I've RTFM on macros* but I can't seem to get the >arguments to evaluate at the correct time. So I'm hoping some kind >soul will show me how this is done. If you are a real masochist I can >even send you the code I've written so far (all 53 lines of it!), so >you can point out what is wrong with it. There are a couple of ways you can do this, depending on what kind of destructuring you want and how close your Lisp is to ANSI Common Lisp. I'm not sure of the intended syntax and semantics of record-case, but I'll assume that that case-like dispatching is done on x using the car of each clause, and that the cadr of each clause specifies destructuring for the cdr of x. If you're after simple destructuring like that provided by lambda, then this kind of approach would work: (defmacro record-case (expr &body body) (let* ((expr-temp (gensym)) (expr-cdr-temp (gensym)) (new-body (mapcar #'(lambda (clause) `(,(car clause) (apply #'(lambda ,(cadr clause) ,@(cddr clause)) ,expr-cdr-temp))) body))) `(let* ((,expr-temp ,expr) (,expr-cdr-temp (cdr ,expr-temp))) (case (car ,expr-temp) ,@new-body)))) Using this definition, (record-case x (add (x y) (+ x y)) (sub (x y) (- x y)) (mul (x y) (* x y)) (div (x y) (/ x y))) Expands to: (LET* ((#:G32 X) (#:G33 (CDR #:G32))) (CASE (CAR #:G32) (ADD (APPLY #'(LAMBDA (X Y) (+ X Y)) #:G33)) (SUB (APPLY #'(LAMBDA (X Y) (- X Y)) #:G33)) (MUL (APPLY #'(LAMBDA (X Y) (* X Y)) #:G33)) (DIV (APPLY #'(LAMBDA (X Y) (/ X Y)) #:G33)))) Note the technique of substituting expr directly into the macroexpansion only once, binding it there to a temporary and using that temporary whenever you want to refer to the value of expr. If you want the more general destructuring provided by macros and your Lisp has destructuring-bind, you could use: (defmacro record-case (expr &body body) (let* ((expr-temp (gensym)) (expr-cdr-temp (gensym)) (new-body (mapcar #'(lambda (clause) `(,(car clause) (destructuring-bind ,(cadr clause) ,expr-cdr-temp ,@(cddr clause)))) body))) `(let* ((,expr-temp ,expr) (,expr-cdr-temp (cdr ,expr-temp))) (case (car ,expr-temp) ,@new-body)))) Now record-case expands to: (LET* ((#:G41 X) (#:G42 (CDR #:G41))) (CASE (CAR #:G41) (ADD (DESTRUCTURING-BIND (X Y) #:G42 (+ X Y))) (SUB (DESTRUCTURING-BIND (X Y) #:G42 (- X Y))) (MUL (DESTRUCTURING-BIND (X Y) #:G42 (* X Y))) (DIV (DESTRUCTURING-BIND (X Y) #:G42 (/ X Y))))) If your lisp doesn't have destructuring-bind or you don't want to use the macro lambda list syntax, I can send you some code I wrote for destructuring in the LOOP macro. However, implementing destructuring (or a simplified version of destructuring-bind) is a fine project for an aspiring hairy-macro writer! Tim Moore moore@cs.utah.edu {bellcore,hplabs}!utah-cs!moore "Ah, youth. Ah, statute of limitations." -John Waters