lisp初体验-Practical Common Lisp笔记-10.自定义宏
一般来说,凡是带有自定义的东东都会显得很专业,不过在lisp中,这才算刚刚开始。至于你信不信,反正我是信了。就让我们开始吧。
说来有些不可理喻,宏之所以难以理解,是因为它在lisp中太过自然(天生的阿),运用起来毫不费力。以至于很容易被误解为一个有意思的函数。事实上,虽然宏真的很像函数,但仅仅是像而已。他们完全不在一个层面上,抽象的层次也大不相同。
一旦理解了宏和函数的区别,恭喜你,晋级了!你将会进入一个全新的层次(这个初等教材已经不适合你了)。不过,既然你还在看,那就让我们继续吧。唉,作者在这里讲了一个故事,就不罗嗦了,这个故事教育我们:如果,你会写宏,那么纵然你一天到晚不呆办公室,你的活也能做完(宏帮你做了)~这就是个奇迹,而且已经发生了。
宏时间和运行时间
理解宏的关键在于可以分清楚宏和普通代码的区别。宏是用来生成普通代码的,而普通代码则是用来被解释、编译的。宏生成普通代码所用时间为宏时间,普通代码(包括宏生成的)编译执行时间称为运行时间。lisp程序运行的先后顺序就是:先执行所有的宏,然后编译执行代码。
比较明显的表现就是,宏时间内是访问不了运行时间内的任何数据:
(defun foo (x) (when (> x 10) (print 'big)))
(defmacro when (condition &rest body) `(if ,condition (progn ,@body)))
(if (> x 10) (progn (print 'big)))
(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 (p 0 19) (format t "~d " p))
(do ((p (next-prime 0) (next-prime (1+ p)))) ((> p 19)) (format t "~d " p))
(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)))
(defmacro do-primes ((var start end) &body body) `(do ((,var (next-prime ,start) (next-prime (1+ ,var)))) ((> ,var ,end)) ,@body))
CL-USER> (macroexpand-1 '(do-primes (p 0 19) (format t "~d " p)))(DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1+ P)))) ((> P 19)) (FORMAT T "~d " P))T
(do-primes (p 0 (random 100)) (format t "~d " p))
CL-USER> (macroexpand-1 '(do-primes (p 0 (random 100)) (format t "~d " p)))(DO ((P (NEXT-PRIME 0) (NEXT-PRIME (1+ P)))) ((> P (RANDOM 100))) (FORMAT T "~d " P))T
(defmacro do-primes ((var start end) &body body) `(do ((ending-value ,end) (,var (next-prime ,start) (next-prime (1+ ,var)))) ((> ,var ending-value)) ,@body))
(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))(let ((ending-value 0)) (do-primes (p 0 10) (incf ending-value p)) ending-value)
(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)))
(do ((ending-value (next-prime 0) (next-prime (1+ ending-value))) (#:g2141 10)) ((> ending-value #:g2141)) (print ending-value))(let ((ending-value 0)) (do ((p (next-prime 0) (next-prime (1+ p))) (#:g2140 10)) ((> p #:g2140)) (incf ending-value p)) ending-value)
(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 ((&rest names) &body body) `(let ,(loop for n in names collect `(,n (gensym))) ,@body))