Это хорошее приложение для простой макрорекурсии:
(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))))))
Тесты:
[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
Макрос должен рассуждать о входном синтаксисе и генерировать код, который выполняет подсчет; вы не должны путаться между генерацией кода и оценкой.
Вы сразу же ошибаетесь, когда делаете это:
`(cond ((null ,forms ...) ...)
вы вводите метасинтаксический расчет (сколько форм у нас в синтаксисе?) В шаблон сгенерированного кода, который будет оцениваться во время выполнения. У вас есть правильные части в этом базовом случае, но они поставлены неправильно. В моем решении у меня есть cond
только в самом теле макроса, а не в обратной кавычке:
(cond ((null forms) ...) ...)
В основном:
(cond (<if the syntax is like this> <generate this>)
(<if the syntax is like that> <generate that>)
...)
Если вы не знаете, что делать, напишите код, который вы хотите написать в макросе. Например:
;; 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))
Хорошо, для этого точного случая мы напишем:
(defmacro count-true (single-form)
`(if ,single-form 1 0))
Готово! Теперь предположим, что мы хотим поддерживать (count-true)
без форм.
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
Когда есть форма, расширение if
остается, но когда форм нет, нам просто нужен постоянный ноль. Легко, сделайте аргумент необязательным:
(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
Наконец, распространяем на N-арную форму:
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))
^^^^^^^^^^ ^^^^^^^^^^
Но! Теперь отметим, что подчеркнутые термины соответствуют выводу одного случая.
(count-true x y) (+ (count-true x) (count-true y))
Что обобщает
(count-true x y z ...) (+ (count-true x) (count-true y z ...))
Это прямо соответствует шаблону генерации кода с car/cdr
рекурсией:
`(+ (count-true ,CAR) (count-true ,*CDR))