Another classic macro-writing MACRO: ONCE-ONLY(另一个经典的用于编写宏的宏:ONCE-ONLY

Another classic macro-writing macro is once-only, which is used to generate code that evaluates certain macro arguments once only and in a particular order. Using once-only, you could write do-primes almost as simply as the original leaky version, like this:

另一个经典的用于编写宏的宏是 once-only,它用来生成以特定顺序仅求值特定宏参数一次的代码。使用 once-only,你几乎可以跟最初的有漏洞版本一样简单地写出 do-primes 来,就像这样:

(defmacro do-primes ((var start end) &body body)
  (once-only (start end)
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
         ((> ,var ,end))
       ,@body)))

However, the implementation of once-only is a bit too involved for a blow-by-blow explanation, as it relies on multiple levels of backquoting and unquoting. If you really want to sharpen your macro chops, you can try to figure out how it works. It looks like this:

尽管如此,但如果详加解释的话,once-only 的实现将远远超出本章的内容,因为它依赖于多层的反引用和解引用。如果真想进一步提高宏技术的话,你可以尝试分析它的工作方式。如下所示:

(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)))))