10 macros. lisp code is expressed as lists, which are lisp objects this makes it possible to write...

21
Functional Programming 10 Macros

Upload: elinor-berry

Post on 16-Dec-2015

248 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: 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

Functional Programming

10 Macros

Page 2: 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

Introduction

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

Page 3: 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

Eval

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

HelloNIL

Page 4: 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

Eval

(defun our-toplevel ( ) (do ( ) (nil) (format t “~%> “) (print (eval (read)))))

The toplevel is also known as a read-eval-print loop → 把所讀到的東西給 evaluate 出來

Page 5: 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

Eval

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

Page 6: 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

Macros

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

> xNIL

Page 7: 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

Macros

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 ( 展開到沒有為止 )

Page 8: 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

Macros

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)

Page 9: 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

Backquote

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)

Page 10: 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

Backquote

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)

Page 11: 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

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

> xNIL

Page 12: 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

Backquote

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 9NIL

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

Page 13: 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

Macro design

想寫一個程式 , 可以 evaluate 他的 body n 次例如 :> (ntimes 10 (princ “.”))……….NIL

如果這樣寫

(defmacro ntimes (n &rest body) ‘(do ((x 0 (+ x 1))) ((>= x ,n)) ,@body))

看起來正確 , 但是某種情況下會有問題

Page 14: 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

Macro design

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

Page 15: 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

Macro design

解決方式 : 用 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 個

Page 16: 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

Macro design

因為前述的 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))))

Page 17: 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

Macro design

看看 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 印出

Page 18: 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

Macro design

(defmacro cah (lst) ‘(car ,lst)) ; 如果 macro 的定義中有可以被 setf 的function

> (let ((x (list ’a ’b ’c))) (setf (cah x) 44) ; 則 macro 也可以被setf x)(44 B C)

Page 19: 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

Macro design 寫一個東西來計算平均

> (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 即計算完成

Page 20: 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

Macro design

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 <trigger> <result>)into(when (not <trigger>) <result>)

Page 21: 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

Macro design

(defmacro when-nil (trigger result) ‘(when (not ,trigger) ,result))

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