Am Ende von Ch 8 in Practical Common Lisp präsentiert Peter Seibel das Makro once-only
. Sein Zweck besteht darin, eine Reihe von subtilen Problemen mit der Variablenbewertung in benutzerdefinierten Makros zu mildern. Hinweis bin ich an dieser Stelle nicht versuchen zu verstehen, wie dieses Makro funktioniert, wie in einigen anderen Beiträgen, sondern nur, wie man sie richtig benutzt:Verwenden des "Once-only" -Makros
(defmacro once-only ((&rest names) &body body)
(let ((gensyms (loop for n in names collect (gensym))))
`(let (,@(loop for g in gensyms collect `(,g (gensym))))
`(let (,,@(loop for g in gensyms for n in names collect ``(,,g ,,n)))
,(let (,@(loop for n in names for g in gensyms collect `(,n ,g)))
,@body)))))
Das Folgende ist eine Probe (falsche) erfundene Makro, der versucht, weisen mehrere Probleme der Variablenbewertung auf. Sie gibt von einigen Delta eine Reihe von ganzen Zahlen iterieren, um den Bereich der Rückkehr:
(defmacro do-range ((var start stop delta) &body body)
"Sample macro with faulty variable evaluations."
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,@body))
Zum Beispiel (do-range (i 1 15 3) (format t "~A " i))
1 4 7 10 13
gedruckt werden soll und dann 14
zurückzukehren.
Die Probleme umfassen 1) Potentialerfassung des zweiten Auftreten von limit
, da es als eine freie Variable auftritt, 2) Potentialerfassung des anfänglichen Auftretens der gebundenen Variablen limit
, da es zusammen mit anderen Variablen, die in einem Ausdruck auftritt erscheint in den Makroparametern, 3) Out-of-Order-Auswertung, da delta
vor stop
ausgewertet wird, obwohl in der Parameterliste stop
vor delta
erscheint, und 4) mehrfache Variablenauswertungen, da stop
und start
mehrfach ausgewertet werden. Wie ich es verstehe, sollte once-only
diese Probleme beheben:
(defmacro do-range ((var start stop delta) &body body)
(once-only (start stop delta limit)
`(do ((,var ,start (+ ,var ,delta))
(limit ,stop))
((> ,var limit) (- ,stop ,start))
,@body)))
jedoch (macroexpand '(do-range (i 1 15 3) (format t "~A " i)))
beschwert sich über limit
eine ungebundene Variable ist. Wenn ich stattdessen auf with-gensyms
umschalte, was nur für Probleme sorgen soll, läuft die Erweiterung ohne Zwischenfälle ab.
Ist dies ein Problem mit dem Makro once-only
? Und löst once-only
wirklich alle oben genannten Probleme (und vielleicht andere)?
Warum haben Sie 'limit' überhaupt ? Könnten Sie nicht alle Anwendungen mit ', stop' in der 'Once-Only'-Version ersetzen? – melpomene
@melpomene Ja, du hast Recht. Aber ich konnte mir keinen besseren, einfachen Weg vorstellen, um die zwei grundlegenden Arten von Variablenerfassungsproblemen in das fehlerhafte Makro einzuführen (aufgelistet als Probleme 1 und 2 oben). Irgendwelche anderen Ideen? – davypough