Esta es una buena aplicación para facilitar la recursión de macros:
(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))))))
Pruebas:
[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
La macro tiene que razonar sobre la entrada sintaxis y generar el código que hace el recuento; no debe mezclarse entre generar el código y evaluar.
Vas mal la derecha del palo cuando estás haciendo esto:
`(cond ((null ,forms ...) ...)
que está empujando el cálculo meta-sintáctica (cuántas formas tenemos en la sintaxis?) En el la plantilla de código generada se evaluará en tiempo de ejecución. Tienes las piezas correctas en este caso base, pero están mal organizadas. En mi solución, tengo el cond
sólo en el propio cuerpo de la macro, no en una comilla inversa:
(cond ((null forms) ...) ...)
Básicamente:
(cond (<if the syntax is like this> <generate this>)
(<if the syntax is like that> <generate that>)
...)
Si usted no sabe qué hacer, escribe el código que quieres que la macro escribaPor ejemplo:
;; 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))
bien, así que para este caso exacto, escribiríamos:
(defmacro count-true (single-form)
`(if ,single-form 1 0))
hecho! Ahora supongamos que queremos admitir (count-true)
sin formularios.
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
Cuando hay una forma, la expansión if
se queda, pero cuando no hay formas, sólo queremos un cero constante. Fácil, hacer que el argumento opcional:
(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
Por último, se extiende a la forma de N-aria:
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))
^^^^^^^^^^ ^^^^^^^^^^
Pero! ahora notamos que los términos subrayados corresponden a la salida del caso individual.
(count-true x y) (+ (count-true x) (count-true y))
que generaliza
(count-true x y z ...) (+ (count-true x) (count-true y z ...))
que corresponde de manera directa a la plantilla de generación de código con car/cdr
recursividad:
`(+ (count-true ,CAR) (count-true ,*CDR))
gracias por la explicación. Ahora veo mi comprensión errónea de la macro expansión. un buen consejo sobre el uso de 'LOOP' en su lugar. Desde que descubrí cómo usar solo una función para esta tarea. –