About List Comprehension
Please refer to wikipedia for the definition and usage of List Comprehension. http://en.wikipedia.org/wiki/List_comprehension
The following is a typical example of the usage of list comprehension:
(setf l '(1 2 3 4 5 6 7 8 9 10)) (collect-list (list x y) (for x in l) (evenp x) (for y in l) (oddp y)) => ((2 1) (2 3) (2 5) (2 7) (2 9) (4 1) (4 3) (4 5) (4 7) (4 9) (6 1) (6 3) (6 5) (6 7) (6 9) (8 1) (8 3) (8 5) (8 7) (8 9) (10 1) (10 3) (10 5) (10 7) (10 9))
collect-listcan be constructed using
with-collectmacro and LOOP (or iterate) macro. Thus it can collect data in the data types which are supported by LOOP and Iterate. In fact, using
loopmakes the implementation quite easy and straightforward.
Here's the implementation of collect-list (based on Loop):
(defmacro collect-list (element &body qualifiers) (labels ((build-form (qualifiers body) (if (not qualifiers) body (let ((first-form (first qualifiers)) (rest-form (rest qualifiers))) (cond ((string-equal (symbol-name (first first-form)) "FOR") (build-for-clause first-form (build-form rest-form body))) (t `(when ,first-form ,(build-form rest-form body))))))) (build-for-clause (form body) `(loop ,@form do ,body))) (let ((collector (gensym "COLLECTOR"))) `(with-collect (,collector) ,(build-form qualifiers `(,collector ,element))))))
And the example will be expanded into:
(WITH-COLLECT (#:COLLECTOR1702) (LOOP FOR X IN TEST DO (WHEN (EVENP X) (LOOP FOR Y IN TEST DO (WHEN (ODDP Y) (#:COLLECTOR1702 (LIST X Y)))))))
Which is also very efficient.
with-collect macro can be found in CLOCC/CLLIB/simple.lisp
(defun permutation (list) (if (null list) '(()) (collect-list (cons x ys) (for x in list) (for ys in (permutation (remove x list))))))
Other ImplementationsFor another take on comprehensions see List comprehensions for Lisp, a LGPL'd general collect macro.
It’ll be handy to use Iterate instead of Loop, in order to take advantage of its flexibility and expressiveness. But some forms like (for y previous x) are something you definitely don’t want to nest. One way to identify that is to construct it as ((for x …) (for y …)).