Wednesday, March 25, 2009

list-of macro

I stumbled on this page and started to solve the problems, Here is solution to the ex7.
list-of is a macro that simplifies collecting lists of values of expressions. Though this description is long, and the macro is powerful, it's actually quite simple and can be implemented with relatively little code.

The general syntax is

(LIST-OF exp generator-or-filter generator-or-filter ...)

It's easiest to explain by starting with simple examples.

> (list-of (1+ x) (x :in '(1 2 3)))
(2 3 4)

exp is (1+ x) and (x :in '(1 2 3)) is a generator. A generator is anything that has the form (variable :in list). This generator generates three values for x, namely 1, 2, and 3. list-of returns a list of the value of (1+ x) for those three values of x.

> (list-of (1+ x) (x :in '(1 2 3)) (oddp x))
(2 4)

The exp and generator are as before, but now I've added the filter (oddp x). A filter is any expression that doesn't look like a generator. The filter says "keep only those values of x that are odd." Hence, list-of only collects values for (1+ x) equal to 1 and 3.

That's it. Any number of generators and filters can be given. They are applied from left to right. If there are two generators, the second repeats itself for every value created by the first, e.g.,

> (setq l '(a b))
(A B)
> (list-of (list x y) (x :in l) (y :in ))
((A A) (A B) (B A) (B B))

Likewise, the filters apply in order.

> (setq l '(1 2 3 4))
(1 2 3 4)
> (list-of (list x y) (x :in l) (oddp x) (y :in l) (evenp y))
((1 2) (1 4) (3 2) (3 4))

This collects (list x y) for every x in l that is odd and every y in l that is even. Notice that

> (list-of (list x y) (x :in l) (y :in l) (oddp x) (evenp y))
((1 2) (1 4) (3 2) (3 4))

returns the same answer, but does more work. Trace oddp to see the difference.

One special case that follows naturally:

  • (list-of exp) simply returns a list of exp.

Note: It'd be more direct to write "the list of x in l that are odd" as

(list-of (x :in l) (oddp x))

rather than

(list-of x (x :in l) (oddp x))

Define list-of so that if no initial expression to collect is given, it uses the variable of the first generator.

Solution:
(defmacro list-of (&rest args)
;functions to deal with generator forms
(defun generator? (x) (and (listp x)(eq (cadr x) :in)))
(defun var-generator (x) (car x))
(defun list-generator (x) (caddr x))
;following function generates appropriate code given
;the expression and list of generators-filters
(defun generate-code (exp gfs)
(let ((code (gensym)))
(defun recurse (gfs)
(cond ((null gfs) `(push ,exp ,code))
((generator? (car gfs))
`(dolist (,(var-generator (car gfs))
,(list-generator (car gfs)))
,(recurse (cdr gfs))))
(t `(if ,(car gfs) ,(recurse (cdr gfs))))))
`(let ((,code nil))
,(recurse gfs) (nreverse ,code))))
;extracting exp and list of generators-filters; then calling
;generate-code for code generation.
(let ((exp (car args))
(generators-filters (cdr args)))
(if (generator? (car args))
(progn (setf exp (var-generator (car args)))
(setf generators-filters args)))
(generate-code exp generators-filters)))

No comments:

Post a Comment