1 / 21

Functional Programming

10 Macros. Functional Programming. 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. Eval. How to generate expressions? Just call list

Download Presentation

Functional Programming

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 10 Macros Functional Programming

  2. 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

  3. 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

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

  5. 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

  6. Macros • The most common way to write programs that write programs is by defining macros • (defmacronil! (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

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

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

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

  10. 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)) • > (setflst ’(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. 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

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

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

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

  15. 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個

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

  17. 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印出

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

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

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

  21. Macro design • (defmacro when-nil (trigger result) ‘(when (not ,trigger) ,result)) • (defmacro when-nil (trigger &rest result) ‘(when (not ,trigger) ,@result))

More Related