expanding a parameter list in a common lisp macro - macros

I'm trying to teach myself common lisp, and as an exercise in macro-writing, I'm trying to create a a macro to define a nested-do loop of arbitrary depth. I'm working with sbcl, using emacs and slime.
To start, I wrote this double-loop macro:
(defmacro nested-do-2 (ii jj start end &body body)
`(do ((,ii ,start (1+ ,ii)))
((> ,ii ,end))
(do ((,jj ,ii (1+ ,jj)))
((> ,jj ,end))
,#body)))
which I could then use as follows:
(nested-do-2 ii jj 10 20 (print (+ ii jj)))
BTW, I originally wrote this macro using gensym to generate the loop counters (ii, jj), but then I realized that the macro was pretty useless if I couldn't access the counters in the body.
Anyway, I would like to generalize the macro to create a nested-do loop that would be nested to an arbitrary level. This is what I've got so far, but it doesn't quite work:
(defmacro nested-do ((&rest indices) start end &body body)
`(dolist ((index ,indices))
(do ((index ,start (1+ index)))
((> index ,end))
(if (eql index (elt ,indices (elt (reverse ,indices) 0)))
,#body))))
which I would like to invoke as follows:
(nested-do (ii jj kk) 10 15 (print (+ ii jj kk)))
However, the list is not being expanded properly, and I end up in the debugger with this error:
error while parsing arguments to DEFMACRO DOLIST:
invalid number of elements in
((INDEX (II JJ KK)))
And in case it's not obvious, the point of the embedded if statement is to execute the body only in the innermost loop. That doesn't seem terribly elegant to me, and it's not really tested (since I haven't been able to expand the parameter list yet), but it's not really the point of this question.
How can I expand the list properly within the macro? Is the problem in the macro syntax, or in the expression of the list in the function call? Any other comments will also be appreciated.
Thanks in advance.

Here's one way to do it - build the structure from the bottom (loop body) up each index:
(defmacro nested-do ((&rest indices) start end &body body)
(let ((rez `(progn ,#body)))
(dolist (index (reverse indices) rez)
(setf rez
`(do ((,index ,start (1+ ,index)))
((> ,index ,end))
,rez)))))

[Aside from the down votes, this actually works and it is beautiful too!]
Just to clearly illustrate the recursive nature of the macro definition, here is a Scheme implementation:
(define-syntax nested-do
(syntax-rules ()
((_ ((index start end)) body)
(do ((index start (+ 1 index)))
((= index end))
body))
((_ ((index start end) rest ...) body)
(do ((index start (+ 1 index)))
((= index end))
(nested-do (rest ...) body)))))
Using the above, as a template, something like this gets it done:
(defmacro nested-do ((&rest indices) start end &body body)
(let ((index (car indices)))
`(do ((,index ,start (1+ ,index)))
((> ,index ,end))
,(if (null (cdr indices))
`(progn ,#body)
`(nested-do (,#(cdr indices)) ,start ,end ,#body)))))
* (nested-do (i j) 0 2 (print (list i j)))
(0 0)
(0 1)
(0 2)
(1 0)
(1 1)
(1 2)
(2 0)
(2 1)
(2 2)
NIL
Note that with all Common-Lisp macros you'll need to use the 'gensym' patterns to avoid variable capture.

Related

Using the `once-only` macro

At the end of Ch 8 in Practical Common Lisp, Peter Seibel presents the once-only macro. Its purpose is to mitigate a number of subtle problems with variable evaluation in user-defined macros. Note I'm not trying to understand at this point how this macro works, as in some other posts, but just how to use it properly:
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,#(loop for g in gensyms collect `(,g (gensym))))
`(let (,,#(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,#(loop for n in names for g in gensyms collect `(,n ,g)))
,#body)))))
The following is a sample (incorrect) contrived macro that attempts to exhibit several variable evaluation problems. It purports to iterate over a range of integers by some delta, returning the range:
(defmacro do-range ((var start stop delta) &body body)
"Sample macro with faulty variable evaluations."
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,#body))
For example, (do-range (i 1 15 3) (format t "~A " i)) should print 1 4 7 10 13 and then return 14.
The problems include 1) potential capture of the second occurrence of limit, since it occurs as a free variable, 2) potential capture of the initial occurrence of the bound variable limit, since it occurs in an expression along with other variables appearing in the macro parameters, 3) out of order evaluation, since delta will be evaluated before stop, even though stop appears before delta in the parameter list, and 4) multiple variable evaluations, since stop and start are evaluated more than once. As I understand it, once-only should fix these problems:
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta limit)
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,#body)))
However, (macroexpand '(do-range (i 1 15 3) (format t "~A " i))) complains about limit being an unbound variable. If I switch instead to with-gensyms, which should take care of problems 1 & 2 above only, the expansion proceeds without incident.
Is this an issue with the once-only macro? And does once-only really solve all the problems outlined above (and perhaps others)?
The ONCE-ONLY macro
To get rid of a warning that N is unused, I would change the macro to:
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for nil in names collect (gensym))))
; changed N to NIL, NIL is ignored
`(let (,#(loop for g in gensyms collect `(,g (gensym))))
`(let (,,#(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,#(loop for n in names for g in gensyms collect `(,n ,g)))
,#body)))))
The purpose of this macro is to make sure that expressions are only evaluated once and in a defined order. For that it will introduce new uninterned variables and will bind the evaluation results to those. Inside he macro the new variables are available. The macro itself is provided to make writing macros easier.
Using ONCE-ONLY in DO-RANGE
Your example use of ONCE-ONLY:
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta limit)
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,#body)))
Why is there LIMIT in the once-only list? limit is undefined there. LIMIT is used inside the ONCE-ONLY form as a symbol, but outside there is no binding.
ONCE-ONLY expects that the list of names is a list of symbols and that these names are bound to forms. In your case limit is a symbol, but it is undefined.
We need to remove limit from the list of names:
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta)
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,#body)))
Now, what to do about LIMIT? Given that once-only provides bindings for the names, including for STOP, we can eliminate the symbol LIMIT and replace its use with ,stop:
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta)
`(do ((,var ,start (+ ,var ,delta)))
((> ,var ,stop) (- ,stop ,start))
,#body)))
Example:
CL-USER 137 > (pprint
(macroexpand
'(do-range (i 4 10 2)
(print i))))
(LET ((#1=#:G2170 4)
(#3=#:G2171 10)
(#2=#:G2172 2))
(DO ((I #1# (+ I #2#)))
((> I #3#) (- #3# #1#))
(PRINT I)))

What is the best way of combining &key and &rest in a lisp macro's lambda list?

I implemented Heap's algorithm using a macro. It's working OK, but I would like to tweak it so it will generate anaphoric or non-anaphoric code on demand. In other words, I would like to have the macro either make an internal copy of the sequence it will permutate or work on a sequence available outside the macro.
My utterly unsatisfactory, downright embarrassing code is:
;; Anaphoric version
;; To make it non-anaphoric, substitute (,var (copy-seq ,vec)) for (,var ,vec)
(defmacro run-permutations (var vec &rest body)
"Executes body for all permutations of vec, which is stored in variable var"
`(let ((,var ,vec))
(labels ((generate (&optional (n (length ,var)))
(if (= n 1)
(progn ,#body)
(progn
(loop for i from 0 below (1- n)
do (progn
(generate (1- n))
(rotatef (aref ,var (if (evenp n) i 0))
(aref ,var (1- n)))))
(generate (1- n))))))
(generate))))
? (run-permutations v "123" (pprint v))
"123"
"213"
"312"
"132"
"231"
"321"
?
I would like to write something that worked like this...
? (setf v "123")
? (run-permutations :anaphoric t v "123" (...do stuff...))
? v
"321"
? (setf v "123")
? (run-permutations v "123" (...do stuff...))
? v
"123"
...but I haven't found a satisfactory combination of &rest and &key or any other approach for writing the lambda list.
So my question is: is there a way of accomplishing that, preferably without writing more code to parse the macro's lambda list? Or is there another, more or less standard (and presumably more elegant) solution out there? I strongly suspect the latter.
Your input is much appreciated. As always, any other comments on the code are appreciated as well.
UPDATE
Brilliant! I opted to use a gensym for n because body is called from within the recursion and I can't see how it could be called from elsewhere—at least not without rewriting everything.
I've also added another feature and a minor optimization. In case you're curious, the updated version is:
(defmacro do-permutations ((var vec &key anaphoric (len (length vec))) &body body)
"Executes body for all permutations of vec, which is stored in variable var.
KEYS:
anaphoric: if defined, modifies var outside the macro, preserves it otherwise
len: number of items that will be permuted, default is the full vector"
(let ((n (gensym)))
`(let ((,var ,(if anaphoric vec `(copy-seq ,vec))))
(labels ((generate (&optional (,n ,len))
(if (= ,n 1)
(progn ,#body)
(let ((n-1 (1- ,n)))
(loop for i from 0 below n-1
do (progn
(generate n-1)
(rotatef (aref ,var (if (evenp ,n) i 0))
(aref ,var n-1))))
(generate n-1)))))
(generate)))))
Finally, I tried to remove theprogn after do but it didn't work because 2 expressions have to be evaluated at that point.
Indent your code correctly:
(defmacro run-permutations (var vec &rest body)
"Executes body for all permutations of vec, which is stored in variable var"
`(let ((,var ,vec))
(labels ((generate (&optional (n (length ,var)))
(if (= n 1)
(progn ,#body)
(progn
(loop for i from 0 below (1- n)
do (progn
(generate (1- n))
(rotatef (aref ,var (if (evenp n) i 0))
(aref ,var (1- n)))))
(generate (1- n))))))
(generate))))
Use something like:
(do-permutations (v "123" :anaphoric t)
(some)
(stuff))
with a macro:
(defmacro do-permutations ((var vec &key anaphoric) &body body)
...)
other names: doing-permutations, with-permutations, ...
Note also that the body can be declared with &body, instead of &rest. The semantics is the same, but one expect it to be indented differently. &body signals that a list of Lisp forms follows.
You also don't need a progn in a loopafter do.
The body sees the variable n. You may think of another place for the body...

Lisp: defmacro with &optional and &body

I wrote a quick and dirty macro to time lisp code. However, the problem I am facing now is that I wanted to include an optional output-stream in the function. However, I can not figure out how to use both the &optional and &body parameters in the defmacro. I looked for examples but found only those for defun which I think I understand. I am not able to figure out why this is failing for me. Any hints:
(defmacro timeit (&optional (out-stream *standard-output*) (runs 1) &body body)
"Note that this function may barf if you are depending on a single evaluation
and choose runs to be greater than one. But I guess that will be the
caller's mistake instead."
(let ((start-time (gensym))
(stop-time (gensym))
(temp (gensym))
(retval (gensym)))
`(let ((,start-time (get-internal-run-time))
(,retval (let ((,temp))
(dotimes (i ,runs ,temp)
(setf ,temp ,#body))))
(,stop-time (get-internal-run-time)))
(format ,out-stream
"~CTime spent in expression over ~:d iterations: ~f seconds.~C"
#\linefeed ,runs
(/ (- ,stop-time ,start-time)
internal-time-units-per-second)
#\linefeed)
,retval)))
This is how I intend to use the code:
(timeit (+ 1 1)) ; Vanilla call
(timeit *standard-output* (+ 1 1)) ; Log the output to stdout
(timeit *standard-output* 1000 (+ 1 1)) ; Time over a 1000 iterations.
I think this, found from the hyperspec, on defmacro is a similar idea.
(defmacro mac2 (&optional (a 2 b) (c 3 d) &rest x) `'(,a ,b ,c ,d ,x)) => MAC2
(mac2 6) => (6 T 3 NIL NIL)
(mac2 6 3 8) => (6 T 3 T (8))
EDIT: Keyword arguments
The usage shown above is clearly flawed. Perhaps, this is better:
(timeit (+ 1 1)) ; Vanilla call
(timeit :out-stream *standard-output* (+ 1 1)) ; Log the output to stdout
(timeit :out-stream *standard-output* :runs 1000 (+ 1 1)) ; Time over a 1000 iterations.
Thanks.
How should that work?
How should it be detected that the first thing is the optional stream?
(timeit a) ; is a the optional stream or an expression to time?
(timeit a b) ; is a the optional stream or an expression to time?
(timeit a b c) ; is a the optional stream or an expression to time?
I would avoid such macro arglists.
Usually I would prefer:
(with-timings ()
a b c)
and with a stream
(with-timings (*standard-output*)
a b c)
The first list gives the optional parameters. The list itself is not optional.
That macro should be easier to write.
Generally it may not be necessary to specify a stream:
(let ((*standard-output* some-stream))
(timeit a b c))
You can implement what you want, but I would not do it:
(defmacro timeit (&rest args)
(case (length args)
(0 ...)
(1 ...)
(otherwise (destructuring-bind (stream &rest body) ...))))
Solution: With a non-optional keyword arglist
(defmacro timeit ((&key
(to-stream *standard-output*)
(with-runs 1))
&body body)
"Note that this function may barf if you are depending on a single evaluation
and choose with-runs to be greater than one. But I guess that will be the
caller's mistake instead."
(let ((start-time (gensym))
(stop-time (gensym))
(temp (gensym))
(retval (gensym))
(elapsed-time (gensym)))
`(let* ((,start-time (get-internal-run-time))
(,retval (let ((,temp))
(dotimes (i ,with-runs ,temp)
(setf ,temp ,#body))))
(,stop-time (get-internal-run-time))
(,elapsed-time (/ (- ,stop-time ,start-time)
internal-time-units-per-second)))
(format ,to-stream
(concatenate 'string
"~CAverage (total) time spent in expression"
" over ~:d iterations: ~f (~f) seconds.~C")
#\linefeed
,with-runs
,elapsed-time
(/ ,elapsed-time ,with-runs)
#\linefeed)
,retval)))
Based on Rainer's comments.
Usage pattern:
(timeit nil (+ 1 1)) ; Vanilla case
(timeit (:to-stream *standard-output*) (+ 1 1)) ; Log to stdout
(timeit (:with-runs 1000) (+ 1 1)) ; Evaluate 1000 times
(timeit (:with-runs 1000 :to-stream *standard-output*) (+ 1 1)) ; Evaluate 1000 times and log to stdout
I've of the general opinion that these kind of arguments should generally be provided in a separate list that is the first argument to the macro. This is especially common in the with- type macros. Some other answers have shown how you can do that, but I think it's also a good macro-writing technique to write a functional version first that implements the main functionality, and to then write a macro version. This one isn't too hard, although the approach here does have the potential to add some time increase for function call overhead.
(defun %timeit (function &optional (runs 1) (stream *standard-output*))
(let ((start (get-internal-run-time))
ret
stop)
(prog1 (dotimes (i runs ret)
(declare (ignorable i))
(setf ret (funcall function)))
(setf stop (get-internal-run-time))
(format stream "~&Time spent in ~a iterations: ~f seconds."
runs
(/ (- stop start) internal-time-units-per-second)))))
(defmacro timeit ((&optional (runs 1) (stream *standard-output*)) &body body)
`(%timeit #'(lambda () ,#body) ,runs ,stream))
CL-USER> (timeit (10000000) (1+ most-positive-fixnum))
Time spent in 10000000 iterations: 0.148 seconds.
4611686018427387904

A lisp function refinement

I've done the Graham Common Lisp Chapter 5 Exercise 5, which requires a function that takes an object X and a vector V, and returns a list of all the objects that immediately precede X in V.
It works like:
> (preceders #\a "abracadabra")
(#\c #\d #r)
I have done the recursive version:
(defun preceders (obj vec &optional (result nil) &key (startt 0))
(let ((l (length vec)))
(cond ((null (position obj vec :start startt :end l)) result)
((= (position obj vec :start startt :end l) 0)
(preceders obj vec result
:startt (1+ (position obj vec :start startt :end l))))
((> (position obj vec :start startt :end l) 0)
(cons (elt vec (1- (position obj vec :start startt :end l)))
(preceders obj vec result
:startt (1+ (position obj vec
:start startt
:end l))))))))
It works correctly, but my teachers gives me the following critique:
"This calls length repeatedly. Not so bad with vectors, but still unnecessary. More efficient and more flexible (for the user) code is to define this like other sequence processing functions. Use :start and :end keyword parameters, the way the other sequence functions do, with the same default initial values. length should need to be called at most once."
I am consulting the Common Lisp textbook and google, but there seem to be of little help on this bit: I don't know what he means by "using :start and :end keyword parameters", and I have no clue of how to "call length just once". I would be grateful if you guys could give me some idea how on to refine my code to meet the requirement that my teacher posted.
UPDATE:
Now I have come up with the following code:
(defun preceders (obj vec
&optional (result nil)
&key (start 0) (end (length vec)) (test #'eql))
(let ((pos (position obj vec :start start :end end :test test)))
(cond ((null pos) result)
((zerop pos) (preceders obj vec result
:start (1+ pos) :end end :test test))
(t (preceders obj vec (cons (elt vec (1- pos)) result)
:start (1+ pos) :end end :test test)))))
I get this critique:
"When you have a complex recursive call that is repeated identically in more than one branch, it's often simpler to do the call first, save it in a local variable, and then use the variable in a much simpler IF or COND."
Also,for my iterative version of the function:
(defun preceders (obj vec)
(do ((i 0 (1+ i))
(r nil (if (and (eql (aref vec i) obj)
(> i 0))
(cons (aref vec (1- i)) r)
r)))
((eql i (length vec)) (reverse r))))
I get the critique
"Start the DO at a better point and remove the repeated > 0 test"
a typical parameter list for such a function would be:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
...
)
As you can see it has START and END parameters.
TEST is the default comparision function. Use (funcall test item (aref vector i)).
Often there is also a KEY parameter...
LENGTH is called repeatedly for every recursive call of PRECEDERS.
I would do the non-recursive version and move two indexes over the vector: one for the first item and one for the next item. Whenever the next item is EQL to the item you are looking for, then push the first item on to a result list (if it is not member there).
For the recursive version, I would write a second function that gets called by PRECEDERS, which takes two index variables starting with 0 and 1, and use that. I would not call POSITION. Usually this function is a local function via LABELS inside PRECEDERS, but to make it a bit easier to write, the helper function can be outside, too.
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(preceders-aux item vector start end test start (1+ start) nil))
(defun preceders-aux (item vector start end test pos0 pos1 result)
(if (>= pos1 end)
result
...
))
Does that help?
Here is the iterative version using LOOP:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(let ((result nil))
(loop for i from (1+ start) below end
when (funcall test item (aref vector i))
do (pushnew (aref vector (1- i)) result))
(nreverse result)))
Since you already have a solution that's working, I'll amplifiy Rainer Joswig's solution, mainly to make related stylistic comments.
(defun preceders (obj seq &key (start 0) (end (length seq)) (test #'eql))
(%preceders obj seq nil start end test))
The main reason to have separate helper function (which I call %PRECEDERS, a common convention for indicating that a function is "private") is to eliminate the optional argument for the result. Using optional arguments that way in general is fine, but optional and keyword arguments play horribly together, and having both in a single function is a extremely efficient way to create all sorts of hard to debug errors.
It's a matter of taste whether to make the helper function global (using DEFUN) or local (using LABELS). I prefer making it global since it means less indentation and easier interactive debugging. YMMV.
A possible implementation of the helper function is:
(defun %preceders (obj seq result start end test)
(let ((pos (position obj seq :start start :end end :test test)))
;; Use a local binding for POS, to make it clear that you want the
;; same thing every time, and to cache the result of a potentially
;; expensive operation.
(cond ((null pos) (delete-duplicates (nreverse result) :test test))
((zerop pos) (%preceders obj seq result (1+ pos) end test))
;; I like ZEROP better than (= 0 ...). YMMV.
(t (%preceders obj seq
(cons (elt seq (1- pos)) result)
;; The other little bit of work to make things
;; tail-recursive.
(1+ pos) end test)))))
Also, after all that, I think I should point out that I also agree with Rainer's advice to do this with an explicit loop instead of recursion, provided that doing it recursively isn't part of the exercise.
EDIT: I switched to the more common "%" convention for the helper function. Usually whatever convention you use just augments the fact that you only explicitly export the functions that make up your public interface, but some standard functions and macros use a trailing "*" to indicate variant functionality.
I changed things to delete duplicated preceders using the standard DELETE-DUPLICATES function. This has the potential to be much (i.e., exponentially) faster than repeated uses of ADJOIN or PUSHNEW, since it can use a hashed set representation internally, at least for common test functions like EQ, EQL and EQUAL.
A slightly modofied variant of Rainer's loop version:
(defun preceders (item vector
&key (start 0) (end (length vector))
(test #'eql))
(delete-duplicates
(loop
for index from (1+ start) below end
for element = (aref vector index)
and previous-element = (aref vector (1- index)) then element
when (funcall test item element)
collect previous-element)))
This makes more use of the loop directives, and among other things only accesses each element in the vector once (we keep the previous element in the previous-element variable).
Answer for your first UPDATE.
first question:
see this
(if (foo)
(bar (+ 1 baz))
(bar baz))
That's the same as:
(bar (if (foo)
(+ 1 baz)
baz))
or:
(let ((newbaz (if (foo)
(+ 1 baz)
baz)))
(bar newbaz))
Second:
Why not start with I = 1 ?
See also the iterative version in my other answer...
The iterative version proposed by Rainer is very nice, it's compact and more efficient since you traverse the sequence only one time; in contrast to the recursive version which calls position at every iteration and thus traverse the sub-sequence every time. (Edit: I'm sorry, I was completely wrong about this last sentence, see Rainer's comment)
If a recursive version is needed, another approach is to advance the start until it meets the end, collecting the result along its way.
(defun precede (obj vec &key (start 0) (end (length vec)) (test #'eql))
(if (or (null vec) (< end 2)) nil
(%precede-recur obj vec start end test '())))
(defun %precede-recur (obj vec start end test result)
(let ((next (1+ start)))
(if (= next end) (nreverse result)
(let ((newresult (if (funcall test obj (aref vec next))
(adjoin (aref vec start) result)
result)))
(%precede-recur obj vec next end test newresult)))))
Of course this is just another way of expressing the loop version.
test:
[49]> (precede #\a "abracadabra")
(#\r #\c #\d)
[50]> (precede #\a "this is a long sentence that contains more characters")
(#\Space #\h #\t #\r)
[51]> (precede #\s "this is a long sentence that contains more characters")
(#\i #\Space #\n #\r)
Also, I'm interested Robert, did your teacher say why he doesn't like using adjoin or pushnew in a recursive algorithm?

What is wrong with the following Common Lisp macro using gensym?

Learning Common Lisp (using GNU CLISP 2.43) .. so might be a noob mistake. Example is the 'print prime numbers between x and y'
(defun is-prime (n)
(if (< n 2) (return-from is-prime NIL))
(do ((i 2 (1+ i)))
((= i n) T)
(if (= (mod n i) 0)
(return NIL))))
(defun next-prime-after (n)
(do ((i (1+ n) (1+ i)))
((is-prime i) i)))
(defmacro do-primes-v2 ((var start end) &body body)
`(do ((,var (if (is-prime ,start)
,start
(next-prime-after ,start))
(next-prime-after ,var)))
((> ,var ,end))
,#body))
(defmacro do-primes-v3 ((var start end) &body body)
(let ((loop-start (gensym))
(loop-end (gensym)))
`(do ((,loop-start ,start)
(,loop-end ,end)
(,var (if (is-prime ,loop-start)
,loop-start
(next-prime-after ,loop-start))
(next-prime-after ,var)))
((> ,var ,loop-end))
,#body )))
do-primes-v2 works perfectly.
[13]> (do-primes-v2 (p 10 25) (format t "~d " p))
11 13 17 19 23
Next I tried using gensym to avoid naming clashes in macro expansion - do-primes-v3. However I'm stuck with a
*** - EVAL: variable #:G3498 has no value
Tried using macro-expand to see if i could spot the mistake but I can't.
[16]> (macroexpand-1 `(do-primes-v3 (p 10 25) (format t "~d " p)))
(DO
((#:G3502 10) (#:G3503 25)
(P (IF (IS-PRIME #:G3502) #:G3502 (NEXT-PRIME-AFTER #:G3502))
(NEXT-PRIME-AFTER P)))
((> P #:G3503)) (FORMAT T "~d " P)) ;
Use DO* instead of DO.
DO Initializes the bindings in a scope where they are not yet visible. DO* initializes the bindings in a scope where they are visible.
In this particular case var needs to reference the other binding loop-start.
You don't actually need the gensym here for avoiding variable capture, because you do not introduce any variables that would be "local to the macro". When you macroexpand your do-primes-v2, you will see that no variable is introduced that didn't exist outside of the macro.
You do need it for a different thing, though: avoiding multiple evaluation.
If you call the macro like this:
(do-primes-v2 (p (* x 2) (* y 3))
(format "~a~%" p))
it expands to
(do ((p (if (is-prime (* x 2))
(* x 2)
(next-prime-after (* x 2))
(next-prime-after p)))
((> p (* y 3))
(format "~a~%" p))
At best, this is inefficient, because those multiplications are done multiple times. However, if you use a function with side effects as inputs, like setf or incf, this can be a big problem.
Either move the binding of your loop-start and loop-end to an enclosing LET block or use DO*. The reason is that all loop variables in DO are bound "in parallel", so for the first binding, the (expanded) loop-start variable does not yet have a binding.
I know this doesn't really answer your question, but I do think it is relevant. In my experience, the type of macro you are attempting to write is a very common one. One problem I have with the way you have approached the problem is that it doesn't handle another common use case: functional composition.
I don't have the time to highlight some of the difficulties you will probably encounter using your macro, I will however highlight that, had you built your prime iterator geared towards functional composition, your macro turns out to be extremely simple, avoiding your question altogether.
Note: I have slightly modified some of your functions.
(defun is-prime (n)
(cond
((< n 2)
nil)
((= n 2)
t)
((evenp n)
nil)
(t
(do ((i 2 (1+ i)))
((= i n) t)
(when (or (= (mod n i) 0))
(return nil))))))
(defun next-prime (n)
(do ((i n (1+ i)))
((is-prime i) i)))
(defun prime-iterator (start-at)
(let ((current start-at))
(lambda ()
(let ((next-prime (next-prime current)))
(setf current (1+ next-prime))
next-prime))))
(defun map-primes/iterator (fn iterator end)
(do ((i (funcall iterator) (funcall iterator)))
((>= i end) nil)
(funcall fn i)))
(defun map-primes (fn start end)
(let ((iterator (prime-iterator start)))
(map-primes/iterator fn iterator end)))
(defmacro do-primes ((var start end) &body body)
`(map-primes #'(lambda (,var)
,#body)
,start ,end))
I too recommend that you look at Series. The generator pattern is also a very common occurrence in lisp programs. You may also want to look at Alexandria, in particular the function ALEXANDRIA:COMPOSE to see what cool stuff you can do with functional composition.
I suggest avoiding DO/DO* and macros altogether and instead going for Series (an implementation of which can be found on series.sourceforge.net).
If that's too complex then consider just generating a list of primes with recursion or a generator (for on-demand generation).