I would like to bind a variable inside a LOOP macro, but only conditionally.
Example:
(loop :for (num div) :in '((1 2) (4 2) (3 0) (1 4))
:when (/= 0 div)
:for res = (/ num div)
:collect num
:do (format T "~A divided by ~A = ~A~%" num div res))
This doesn't work as written:
:FOR does not introduce a LOOP clause that can follow WHEN.
current LOOP context: :FOR RES.
[Condition of type SB-INT:SIMPLE-PROGRAM-ERROR]
Is there a way to do this inside a single loop call? Any solutions I can think of, involve breaking out of the loop somehow which has considerable drawbacks. Among others you lose access to the loop context (:collect etc).
You can't do that with loop
. You can work around it as below if you have to use loop
, although coredump's answer is better since a variable binding that is used exactly once might as well not exist.
(loop for (num div) in '((1 2) (4 2) (3 0) (1 4))
for q? = (and (not (zerop div)) (/ num div))
when q?
collect num
and do (format T "~A divided by ~A = ~A~%" num div q?))
However you can also just write Lisp rather than loop
's fragile pseudo-fortran. The following uses Tim Bradshaw's collecting
macro to factor value collection out of a looping construct (it also collects the quotient rather than numerator, so that there is some purpose to the binding):
(collecting
(dolist (v '((1 2) (4 2) (3 0) (1 4)))
(destructuring-bind (numerator denominator) v
(unless (zerop denominator)
(let ((quotient (/ numerator denominator)))
(collect quotient)
(format T "~A divided by ~A = ~A~%" numerator denominator quotient))))))
If the combination of iteration and destructuring is something you do a lot, then (using, this time, metatronic macros to make things a little easier):
(defmacro/m destructuring-dolist ((ll list &optional (value 'nil)) &body forms)
`(dolist (<v> ,list ,value)
(destructuring-bind ,ll <v>
,@forms)))
And now
(collecting
(destructuring-dolist ((numerator denominator) '((1 2) (4 2) (3 0) (1 4)))
(unless (zerop denominator)
(let ((quotient (/ numerator denominator)))
(collect quotient)
(format T "~A divided by ~A = ~A~%" numerator denominator quotient)))))
If you want the numerators and the quotients, well:
(with-collectors (numerator quotient)
(destructuring-dolist ((numerator denominator) '((1 2) (4 2) (3 0) (1 4)))
(unless (zerop denominator)
(let ((quotient (/ numerator denominator)))
(quotient quotient)
(numerator numerator)
(format T "~A divided by ~A = ~A~%" numerator denominator quotient)))))
And of course, if you want to, now you can rely on the fact that you actually have fully-fledged destructuring lambda lists rather than whatever loop
supports:
(with-collectors (numerator quotient)
(destructuring-dolist ((numerator denominator &aux
(valid (not (zerop denominator)))
(quotient (when valid (/ numerator denominator))))
'((1 2) (4 2) (3 0) (1 4)))
(when valid
(quotient quotient)
(numerator numerator)
(format T "~A divided by ~A = ~A~%" numerator denominator quotient))))