This is a nice application for easy macro recursion:
(defmacro count-true (&rest forms)
(cond
((null forms) 0)
((endp (rest forms)) `(if ,(first forms) 1 0))
(t `(+ (count-true ,(first forms)) (count-true ,@(rest forms))))))
Tests:
[2]> (count-true)
0
[3]> (count-true nil)
0
[4]> (count-true t)
1
[5]> (count-true nil t)
1
[6]> (count-true t nil)
1
[7]> (count-true t t)
2
[8]> (count-true nil nil)
0
[9]> (macroexpand '(count-true))
0 ;
T
[10]> (macroexpand '(count-true x))
(IF X 1 0) ;
T
[11]> (macroexpand '(count-true x y))
(+ (COUNT-TRUE X) (COUNT-TRUE Y)) ;
T
[12]> (macroexpand '(count-true x y z))
(+ (COUNT-TRUE X) (COUNT-TRUE Y Z)) ;
T
The macro has to reason about the input syntax and generate the code which does the counting; you must not get mixed up between generating the code and evaluating.
You're going wrong right off the bat when you're doing this:
`(cond ((null ,forms ...) ...)
you're pushing the meta-syntactic calculation (how many forms do we have in the syntax?) into the generated code template be evaluated at run-time. You have the right pieces in this base case, but they are staged wrong. In my solution, I have the cond
just in the macro body itself, not in a backquote:
(cond ((null forms) ...) ...)
Basically:
(cond (<if the syntax is like this> <generate this>)
(<if the syntax is like that> <generate that>)
...)
If you don't know what to do, write out the code that you want the macro to write. For instance:
;; I want this semantics:
(if (blah) 1 0) ;; count 1 if (blah) is true, else 0
;; But I want it with this syntax:
(count-true (blah))
Okay, so for this exact case, we would write:
(defmacro count-true (single-form)
`(if ,single-form 1 0))
Done! Now suppose we want to support (count-true)
with no forms at all.
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
When there is a form, the if
expansion stays, but when there are no forms, we just want a constant zero. Easy, make the argument optional:
(defmacro count-true (&optional (single-form nil have-single-form))
(if have-single-form
`(if ,single-form 1 0) ;; same as before
0)) ;; otherwise zero
Finally, extend to N-ary form:
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
(count-true x y) (+ (if x 1 0) (if y 1 0))
^^^^^^^^^^ ^^^^^^^^^^
But! now we note that the underlined terms correspond to the output of the single case.
(count-true x y) (+ (count-true x) (count-true y))
Which generalizes
(count-true x y z ...) (+ (count-true x) (count-true y z ...))
That corresponds in a straightforward way to the code generation template with car/cdr
recursion:
`(+ (count-true ,CAR) (count-true ,*CDR))
LOOP
instead. ive since figured out how to use just a function for this task. – Tallula