宏2

; 生成以后可执行的代码。展开期和运行期
; 很多时候是,重构的时候用到宏了
; 有了事例调用和预想的展开式,宏不应该泄露实现细节
(defmacro name (parameter*)
  "optional documentation string."
  body-form*)
; 编写宏的步骤:
; 编写示例的宏调用和展开的代码,反之依然
; 编写从示例的参数中生成手写展开式的代码
; 确保宏抽象不产生泄露
(defun primep (number)
  (when (> number 1)
    (loop for fac from 2 to (isqrt number) never (zerop (mod number fac)))))
(defun next-prime (number)
  (loop for n from number when (primep n) return n))
; 调用实例代码
(do-primes (x 0 19)
  (format t "~d" x))
; 无宏的写法
(do ((p (next-prime 0) (next-prime (1+ p))))
  ((> p 19))
  (format t "~d " p))
;;;;我需要熟练掌握do的用法,找一些联系做做,……
; 宏形参
; 在这里宏的参数是一个列表可以这样写宏
(defmacro do-primes (var-and-range &rest body)
  (let ((var (first var-and-range))
       (start (second var-and-range))
       (end (third var-and-range)))
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
    ((> ,var ,end))
  ,@body)))
; 宏形参列表是结构列表,会自动分拆,而且可以使用&body作为&rest的同义词
; 这里可以这样写
(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
    ((> ,var ,end))
    ,@body))
; 宏展开形式
(macroexpand-1 '(do-primes (p 0 19) (format t "~d " p))
 
; 漏洞
(do-primes (p 0 (random 100))
  (format t "~d " p))
; 以上表达式展开后会在循环中多次求值(random 100),必须避免多重求值的情况
; 引入ending-value
(defmacro do-primes ((var start end) &body body)
  `(do ((ending-value ,end)
        (,var (next-prime ,start) (next-prime (1+ ,var))))
       ((> ,var ,ending-value))
    ,@body))
; 求值顺序问题var的求值书序是先start 后end。参数传递顺序和宏调用顺序相反
(defmacro do-primes ((var start end) &body body)
  `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
        (ending-value ,end))
       ((> ,var ,ending-value))
    ,@body))
; 如果调用
; (do-primes (ending-value 0 10)
; (print ending-value))
; 内部实现细节暴漏,展开后变量掩盖了
; 函数gensym在每次调用返回唯一符号
 
(defmacro do-primes ((var start end) &body body)
  (let ((ending-value-name (gensym)))
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
          (,ending-value-name ,end))
         ((> ,var ,ending-value-name))
          ,@body)))
 
; 三个原则:
; 展开式中的任何子形式,求值顺序与宏调用的子形式相同
; 子形式之辈求值一次,用创建变量实现
; 展开式中用到的变量名用GENSYM来实现
 
 
; 编写宏的宏
; 引入变量使用let,可以用宏实现
(defmacro do-primes ((var start end) &body body)
  (with-gensyms (ending-value-name)
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))
          (,ending-value-name ,end))
         ((> ,var ,ending-value-name))
          ,@body)))
 
(defmacro with-gensyms ((&reset names) &body body)
  `(let ,(loop for n in names collect `(,n (gensym)))
    ,@body))
; once-only宏用于生成特定顺序仅求职特定宏参数一次的代码
(defmacro do-primes ((var start end) &body body)
  (once-only (start end)
    `(do ((,var (next-prime ,start) (next-prime (1+ ,var))))
         ((> ,var ,end))
         ,@body)))




posted @ 2012-04-11 08:44  舜耕山翁  阅读(199)  评论(0编辑  收藏  举报