Presentation is loading. Please wait.

Presentation is loading. Please wait.

10 Macros.  Lisp code is expressed as lists, which are Lisp objects  This makes it possible to write programs that would write programs  This lecture.

Similar presentations


Presentation on theme: "10 Macros.  Lisp code is expressed as lists, which are Lisp objects  This makes it possible to write programs that would write programs  This lecture."— Presentation transcript:

1 10 Macros

2  Lisp code is expressed as lists, which are Lisp objects  This makes it possible to write programs that would write programs  This lecture shows how to cross the line from expressions to code

3  How to generate expressions?  Just call list  How to treat expressions as code?  Use the function EVAL  > (eval ’(+ 1 2 3)) 6  > (eval ’(format t “Hello”)) Hello NIL

4  (defun our-toplevel ( ) (do ( ) (nil) (format t “~%> “) (print (eval (read)))))  The toplevel is also known as a read-eval-print loop → 把所讀到的東西給 evaluate 出來

5  Calling eval is one way to cross the line between lists and code. However, it’s not a very good way:  It’s inefficient  The expression is evaluated with no lexical context ▪ > (let ((x 1)) (eval ’(+ x 3))) *** - EVAL: variable X has no value

6  The most common way to write programs that write programs is by defining macros  (defmacro nil! (x) (list ’setf x nil)) ; 用 list 來組一個 list  A call of the form (nil! a) will be translated into (setf a nil)  > (nil! x) NIL  > x NIL

7  To test a function, we call it, but to test a macro, we look at its expansion  > (macroexpand-1 ’(nil! x)) (SETF X NIL) T  A macro call can expand into another macro call. When the compiler (or toplevel) encounters a macro call, it simply keeps expanding it until it is no longer one ( 展開到沒有為止 )

8  When you use defmacro, you’re defining a function much like:  (lambda (expr) (apply #’(lambda (x) (list ’setf x nil)) (cdr expr)))  若 input 是 (nil! a) 則會 return (setf a nil)

9  Like a regular quote, a backquote alone protects its argument from evaluation  > ‘(a b c) (A B C)  The advantage of backquote is: within a backquote expression, you can use, (comma) and,@ (comma-at) to turn evaluation back  > (setf a 1 b 2) 2 > ‘(a is,a and b is,b) (A IS 1 AND B IS 2)

10  By using backquote instead of a call to list, we can write macro definitions that look like the expressions they will produce  (defmacro nil! (x) ‘(setf,x nil))  > (setf lst ’(a b c)) (A B C) > ‘(lst is,lst) (LST IS (A B C)) > ‘(its elememts are,@lst) (ITS ELEMENTS ARE A B C)

11  > (setf x ’(1 2)) (1 2) > ‘(x,x ’,x,@x) (X (1 2) ’(1 2) 1 2)  (defmacro test (x) (setf x nil))  (test a) → 會被展開為 NIL  > (macroexpand ’(test a)) NIL; T  (defmacro test2 (x) ’(setf x nil))  > (macroexpand ’(test2 a)) (SETF X NIL); T  > (test2 a) NIL  > a *** - EVAL: variable A has no value  > x NIL

12  Comma-at is useful in macros that have rest parameters representing, for example, a body of code  Suppose we want a while macro that will evaluate its body so long as an initial test expression remains true  > (let ((x 0)) (while (< x 10) (princ x) (incf x))) 0 1 2 3 4 5 6 7 8 9 NIL  We can define such a macro by using a rest parameter to collect a list of the expressions in the body, then using comma-at to splice this list into the expansion: (defmacro while (test &rest body) ‘(do ( ) ((not,test)),@body))

13  想寫一個程式, 可以 evaluate 他的 body n 次 例如 : > (ntimes 10 (princ “.”)) ………. NIL  如果這樣寫 (defmacro ntimes (n &rest body) ‘(do ((x 0 (+ x 1))) ((>= x,n)),@body)) 看起來正確, 但是某種情況下會有問題

14  若 macro 裡頭的變數名稱跟原來程式中的衝到, 例如 : > (let ((x 10)) (ntimes 5 (setf x (+ x 1))) x) 10  (let ((x 10)) (do ((x 0 (+ x 1))) ((>= x 5)) (setf x (+ x 1))) x)

15  解決方式 : 用 gensym (defmacro ntimes (n &rest body) (let ((g (gensym))) ‘(do ((,g 0 (+,g 1))) ((>=,g,n)),@body))) 但是, 另一個問題 : 如果第一個參數 n 用一個 expression 取代, 那麼 每次做到有 n 地方, 都會 evaluate 一次, 例如 : 若第一個參數為 (setf v (- v 1)), 亦即, > (let ((v 10)) (ntimes (setf v (- v 1)) (princ “.”))) ….. NIL 我們本來希望有 9 個點, 現在只剩 5 個

16  因為前述的 macro 會被展成 : (let ((v 10)) (do ((#:g1 0 (+ #:g1 1))) ((>= #:g1 (setf v (- v 1)))) (princ “.”))) 迴圈每次做到 ((>= #:g1 (setf v (- v 1)))) v 的值都會變化, 因此 #:g1 不再是跟 9 比較 因此, 我們修正為 : (defmacro ntimes (n &rest body) (let ((g (gensym)) (h (gensym))) ‘(let ((,h,n)) ; 先把 n 算好給 h, 使得迴圈中不會動到 (do ((,g 0 (+,g 1))) ((>=,g,h)),@body))))

17  看看 Common Lisp 裏頭內建的 macro  > (pprint (macroexpand-1 ’(cond (a b) (c d e) (t f)))) (IF A B (IF C (PROGN D E) F))  pprint 可以以縮排形式將 code 印出

18  (defmacro cah (lst) ‘(car,lst)) ; 如果 macro 的定義中有可以被 setf 的 function  > (let ((x (list ’a ’b ’c))) (setf (cah x) 44) ; 則 macro 也可以被 setf x) (44 B C)

19  寫一個東西來計算平均  > (avg 2 4 8) 14/3  寫成 function ▪ (defun avg (&rest args) (/ (apply #’+ args) (length args)))  寫成 macro ▪ (defmacro avg (&rest args) ‘(/ (+,@args),(length args)))  若用 function,args 的長度是在 run-time 時計算, 而若用 macro, 則在 compile-time 即計算完成

20  Homework ▪ Imagine that the designers of LISP had forgotten to include the UNLESS primitive. Now you decide to define your own UNLESS primitive, which you call WHEN- NIL. Define WHEN-NIL such that it translates (when-nil ) into (when (not ) )

21  (defmacro when-nil (trigger result) ‘(when (not,trigger),result))  (defmacro when-nil (trigger &rest result) ‘(when (not,trigger),@result))


Download ppt "10 Macros.  Lisp code is expressed as lists, which are Lisp objects  This makes it possible to write programs that would write programs  This lecture."

Similar presentations


Ads by Google