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