Questa è una simpatica applicazione per un facile ricorsione macro:
(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))))))
Test:
[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 deve ragionare sull'ingresso sintassi e generare il codice che esegue il conteggio; non devi essere confuso tra la generazione del codice e la valutazione.
Stai andando storto destra fuori del blocco, quando si sta facendo questo:
`(cond ((null ,forms ...) ...)
si sta spingendo il calcolo meta-sintattica (quante forme cosa abbiamo nella sintassi?) Nel il modello di codice generato deve essere valutato in fase di esecuzione. Hai i pezzi giusti in questo caso base, ma sono messi in scena male. Nella mia soluzione, ho la cond
solo nel corpo macro in sé, non in un backquote:
(cond ((null forms) ...) ...)
In sostanza:
(cond (<if the syntax is like this> <generate this>)
(<if the syntax is like that> <generate that>)
...)
Se non sai cosa fare, scrivere il codice che vuoi che la macro scriva.Per esempio:
;; 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, quindi per questo caso esatto, ci avrebbe scritto:
(defmacro count-true (single-form)
`(if ,single-form 1 0))
Fatto! Ora supponiamo di voler supportare (count-true)
senza alcun modulo.
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
Quando c'è un modulo, l'espansione if
rimane, ma quando non ci sono forme, vogliamo solo uno zero costante. Facile, rendere l'argomento opzionale:
(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
Infine, si estendono a forma di N-ary:
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))
^^^^^^^^^^ ^^^^^^^^^^
Ma! ora notiamo che i termini sottolineati corrispondono all'output del singolo caso.
(count-true x y) (+ (count-true x) (count-true y))
che generalizza
(count-true x y z ...) (+ (count-true x) (count-true y z ...))
che corrisponde in modo diretto al modello di generazione di codice con car/cdr
ricorsione:
`(+ (count-true ,CAR) (count-true ,*CDR))
Grazie per la spiegazione. Vedo ora la mia errata comprensione dell'espansione macro. buon consiglio sull'uso di 'LOOP' invece. perché ho capito come usare solo una funzione per questo compito. –