1 COSC generating functions, templates, and macros Yves Lespérance Adapted from Peter Roosen-Runge
2 COSC midterm October 20 bring photo ID closed book; no access to cellphones, PDAs, laptops, etc. test will be based on the material in the textbook (Ch. 2-8, 10-12, 14), lectures, and readings
3 COSC constructing a pumper Automating Ch. 11 pumper will create a tail-recursive function and a main function to call it, with arguments: function name, the initial result, and a 'construction' function f f constructs a new (partial) result from a previous (partial) result, with the name of f = |HELP-name|
4 COSC pumper in action Example: construct both tail-recursive rev (reverse) and its helper function (pumper 'rev NIL ; initial 'CONS ; used to construct result ) recall in rev2 result --> (CONS (FIRST list) result)
5 COSC numerical example sum of squares: (sumsq list) slow version: (+ (* (FIRST list) (FIRST list)) (sumsq (REST list)) or define sumsq and |Help-SUMSQ| (pumper 'sumsq 0 '(LAMBDA (x y) (+ (* x x) y)) )
6 COSC the main function code to construct the main function (DEFUN makeMain (name helper initial) (EVAL (LIST 'DEFUN name '(arglist) (LIST helper 'arglist initial)) ) )
7 COSC make the helper (DEFUN makeHelp (helper f) (EVAL (LIST 'DEFUN helper '(arglist result) (LIST 'IF '(ENDP arglist) 'result (LIST helper '(REST arglist) (LIST ’FUNCALL (LIST 'FUNCTION f) (LIST 'FIRST arglist) result))))) )
8 COSC demo >(makehelp 'helpfun 'CONS) (defun helpfun (arglist result) (if (endp arglist) result (helpfun (rest arglist) (FUNCALL (function CONS) (first arglist) result))))
9 COSC assembling the pieces (DEFUN pumper (name initial f) (LET ((helper (INTERN (FORMAT nil "Help-~a" name)))) (makeMain name helper initial) (makeHelp helper f) )
10 COSC using templates use template for expressions involving substitutions of values for variables into a fixed structure Lisp's substitution operators backquote ` : means don’t evaluate unless specified comma, :,expr means evaluate see form-cons.html
11 COSC template example (DEFUN makeMain (name helper initial) (EVAL ‘(DEFUN,name (arglist) (,helper arglist,initial))) )
12 COSC the complete pumper (DEFUN pumper (name initial f) (LET ((helper (INTERN (FORMAT nil "Help-~a" name)))) (EVAL `(DEFUN,name (arglist) (,helper arglist,initial))) (EVAL `(DEFUN,helper (arglist result) (IF (ENDP arglist) result (,helper (REST arglist) (FUNCALL (FUNCTION,f) (FIRST arglist) result)))) )
13 COSC pumper demo tail-recursive reverse >(pumper 'rev NIL 'CONS) |Help-REV| >(rev '(a b c)) (C B A) (symbol-function '|Help-REV|) = (LAMBDA-BLOCK |Help-REV| (ARGLIST RESULT) (IF (ENDP ARGLIST) RESULT (|Help-REV| (REST ARGLIST) (FUNCALL #'CONS (FIRST ARGLIST) RESULT))))
14 COSC closures A function may be defined in one context and used in another. If it contains free variables, this may cause problems. Lisp avoids this by keeping a record of the environment where the function was defined in a closure.
15 COSC closure e.g. (DEFUN gen_adder (n) (FUNCTION (LAMBDA (x) (+ x n)))) (DEFUN eg (n k) (FUNCALL (gen_adder k) n)) >(eg 5 7) 12
16 COSC macros
17 COSC sort of like functions, but.. one of the most interesting but tricky aspects of Lisp. unlike functions, macros don't evaluate their arguments they compute on unevaluated expressions just uninterpreted data structures: binary trees 'dot' at the root of every sub-tree atoms at the leaves macros can be expanded once when function that uses macros is defined or compiled
18 COSC COND is an example >(DESCRIBE ’COND) … special operator with macro definition, has 1 property SYSTEM::MACRO. For more information, evaluate (SYMBOL-PLIST ’COND)… >(SYMBOL-PLIST ’COND) (SYSTEM::MACRO # )
19 COSC step macro evaluation takes two steps expansion s-expression evaluation value MACROEXPANDEVAL >(MACROEXPAND '(COND ((NULL x) 1) ((NULL y) 2) (:OTHERWISE 3))) (IF (NULL X) 1 (IF (NULL Y) 2 (IF :OTHERWISE 3 NIL)))
20 COSC e.g. flambda often write (FUNCTION (LAMBDA …)); define a macro flambda that abbreviates this. (DEFMACRO flambda (&REST args) (LIST ’FUNCTION (CONS 'LAMBDA args))) ; args will be a list (MACROEXPAND ’(FLAMBDA (X Y) (CONS X Y))) #’(LAMBDA (X Y) (CONS X Y))
21 COSC using a template (DEFMACRO flambda (&REST args) `(FUNCTION (LAMBDA,args)))
22 COSC e.g. 2+ (DEFMACRO 2+ (ARG) `(1+ (1+,arg))) >(MACROEXPAND ’(2+ x)) (1+ (1+ x)) T >(2+ (+ 3 5)) 10
23 COSC destructuring u Macros often destructure their arguments, i.e. extract some of their parts; this is easier if we use structured parameters, lists with named parts >(DEFMACRO cross ((a b) (c d)) `((,a,d) (,b,c))) (MACROEXPAND '(cross (one two) (three four))) ((one four) (two three))
24 COSC fancier example: (DEFMACRO crossdot ((a. b) (c. d)) `((,a,d) (,b,c))) (MACROEXPAND '(crossdot (one two three) (four five six))) ((one (five six)) ((two three) four) what's happening? what are a, b, c & d bound to?
25 COSC use of macros support abstraction at little cost, e.g. get- state-field can add abbreviations that make programming easier, e.g. COND, AND for more than 2 arguments, loop constructs, etc. can define embedded languages within Lisp with little execution cost caveat: user must learn the syntax expected by the macros
26 COSC macros vs. functions a macro is treated internally as a function of one argument order of evaluation is different, outside- in rather than inside-out, e.g. >(MACROEXPAND ’(2+ (2+ 5)) (1+ (1+ (2+ 5))) T >(2+ (2+ 5)) 9
27 COSC more examples see roExamples.html