I don't really see a need for it, or why would you implement it in such a verbose manner, the first is roughtly equivalent to:
(dotimes (,var ,count) (awhen ,test (return (progn ,@body))))
while the second is roughly equivalent to
(loop for ,var from ,start to ,count do (awhen ,test (return (progn ,@body))))
Of course, that's just in a macro, however in real code, some of those do usually go away (progn or the return, or even the awhen).
It took me longer to understand what your macro was doing than to write that code, however if this pattern is very common for your code, it might be worth using the macro.
There's only a few problems with it:
- The macro can generate a lot of duplicate code for larger values of
count
- It will only work for constant
count's, while my
LOOP or
DOTIMES version would work with variable
count's too.
- Binding it to the result of the test (anaphoric ``it'') is a hygiene problem, but some people are fine with it. A hygienic solution is to pass the name of the variable you want binded in an argument (the right way to call such a macro would be
bcondtimes).
- I don't really have
>>3's third complaint, however a functional version of this macro is possible (and it's also considered an acceptable macro technique), it would go like this:
;;; well-known anaphoric when implementation
(defmacro awhen (test &body body)
`(let ((it ,test)) (when it ,@body)))
;;; functional version of condtimes
(defun condtimes* (count test then &optional (start 0))
(loop for var from start to count do
(awhen (funcall test count) (return (funcall then var it)))))
;;; condtimes implemented through the functional version
(defmacro acondtimes ((var count &optional (start 0)) test &body then)
`(condtimes* ,count
#'(lambda (,var) ,test)
#'(lambda (,var it)
(declare (ignorable it))
,@then)
,start))