iterationcommon-lisppractical-common-lisp

How to translate a LOOP into a DO inside a macro (common lisp)?


I'm currently reading through Seibel's "Practical common lisp" and found this example macro:

(defmacro check (&rest forms)
 `(progn
     ,@(loop for f in forms collect `(do-stuff ,f ',f))

(defun test ()
    (check ( (= (+ 1 2 ) 3) (= (+ 1 2 ) 4 )))
 )

do-stuff simply then format the two args in a certain way allowing for 'testing' the truth of a form, but that's not important for my question.

What I was interested in was to translate the loop into a DO, unfortunately, I'm totally lacking in the necessary skill to do so:

(defmacro check (&rest forms)
`(progn
    ,@(do ((index 0 (list-length forms))
            (accumulator nil))
        ((= index (list-length forms)) accumulator)
         (push `(do-stuff ,(nth index forms) ',(nth index forms)) accumulator)
         ))

This does the job, I can also do this (put every form into a variable inside the do):

(defmacro check (&rest forms)
`(progn
    ,@(do* ((index 0 (list-length forms))
            (accumulator nil)
            (f (nth index forms) (nth index forms)))
        ((= index (list-length forms)) accumulator)
         (push `(do-stuff ,f ',f) accumulator)
         ))

My problem is the following :

Is there a more efficient way to write this do loop ? Is this a good way to implement it ?

Something in the LOOP version is making me wonder if there is not a simple way to extract an element of a list without the need to define an index variable, or to regroup the COLLECTED elements without the need to define an accumulator list...


Solution

  • If you use do you shouldn't use nth. Just iterate over the list, not the indexes.

    (do ((l forms (cdr l))
         (accumulator nil))
        ((null l) (nreverse accumulator))
      (let ((f (car l)))
        (push `(do-stuff ,f ',f) accumulator)))
    

    You can also use the built-in dolist:

    (let ((accumulator nil))
      (dolist (f forms (nreverse accumulator))
        (push `(do-stuff ,f ',f) accumulator)))
    

    Finally there's mapcar:

    (mapcar (lambda (f) `(do-stuff ,f ',f)) forms)