Ceci est une application agréable pour récursion macro facile:
(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
La macro doit raisonner sur l'entrée syntaxe et générer le code qui effectue le comptage; vous ne devez pas confondre entre générer le code et évaluer.
Vous allez mal dès le départ quand vous faites ceci:
`(cond ((null ,forms ...) ...)
vous poussez le calcul de méta-syntaxique (combien de formes-nous dans la syntaxe?) Dans la Le code généré doit être évalué lors de l'exécution. Vous avez les bonnes pièces dans ce cas de base, mais ils sont mal agencés. Dans ma solution, je le cond
juste dans le corps de la macro elle-même, et non pas dans un backquote:
(cond ((null forms) ...) ...)
En gros:
(cond (<if the syntax is like this> <generate this>)
(<if the syntax is like that> <generate that>)
...)
Si vous ne savez pas quoi faire, écrire le code que vous voulez que la macro écrive.Par exemple:
;; 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))
Ok, donc pour ce cas précis, nous écrire:
(defmacro count-true (single-form)
`(if ,single-form 1 0))
Terminé! Maintenant supposons que nous voulons soutenir (count-true)
avec aucune forme du tout.
Wanted Syntax Translation
(count-true) 0
(count-true x) (if x 1 0)
Quand il y a une forme, le reste l'expansion if
, mais quand il n'y a pas de formes, nous voulons juste un zéro constant. Facile, faire l'argument optionnel:
(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
Enfin, étendre à la forme N-aire:
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))
^^^^^^^^^^ ^^^^^^^^^^
Mais! nous notons maintenant que les termes soulignés correspondent à la sortie du cas unique.
(count-true x y) (+ (count-true x) (count-true y))
qui généralise
(count-true x y z ...) (+ (count-true x) (count-true y z ...))
Cela correspond d'une manière simple au modèle de génération de code avec car/cdr
récursion:
`(+ (count-true ,CAR) (count-true ,*CDR))
Merci pour l'explication. Je vois maintenant ma compréhension erronée de l'expansion macro. bon conseil sur l'utilisation de 'LOOP' à la place. ive depuis compris comment utiliser juste une fonction pour cette tâche. –