´
Introduccion
Backquote
Definiendo macros
Probando macros
´ II
Metodolog´ıas de Programacion
Macros en Lisp
´
Dr. Alejandro Guerra-Hernandez
Departamento de Inteligencia Artificial
Facultad de F´ısica e Inteligencia Artificial
[email protected]
http://www.uv.mx/aguerra
Maestr´ıa en Inteligencia Artificial 2012
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
Programas que escriben programas
I
I
´ de una macro es esencialmente el de una
La definicion
´ que genera codigo
´
funcion
Lisp –un programa que genera
programas.
´ produce un resultado,
Diferencia importante: Una funcion
´ que al ser
pero una macro produce una expresion
evaluada produce un resultado.
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplo: nil!
I
Supongan que queremos escribir la macro nil! que
asigna a su argumento el valor nil. Queremos que
(nil! x) tenga el mismo efecto que (setq x nil)
CL-USER> (defmacro nil! (var)
(list ’setq var nil))
NIL!
CL-USER> (nil! x)
NIL
CL-USER> x
NIL
1
2
3
4
5
6
7
I
´
¿Como
evalua
´ Lisp la llamada en la l´ınea 4?
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
´ de una macro
Evaluacion
I
Lisp identifica a nil! como una macro y:
I
I
´ especificada por la definicion
´ de la
Construye la expresion
´ lo que
marco en la fase conocida como macro-expansion,
´ (setq x nil); y entonces
genera la expresion
´ obtenida en lugar de la llamada original
Evalua la expresion
a la macro, esto es, se evalua
´ (setq x nil), como si el
programador la hubiese tecleado.
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
´ solo
´ en algunas partes
Deteniendo la evaluacion
I
1
2
3
4
5
6
7
8
´ especial de nuestro conocido
El backquote es una version
quote, que puede ser usado para definir moldes de
expresiones Lisp.
CL-USER 3
(A B C)
CL-USER 4
(A B C)
CL-USER 5
3
CL-USER 6
(A (2 C))
> (list ’a ’b ’c)
> ‘(a b c)
> (setf a 1 b 2 c 3)
> ‘(a (,b c))
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
Ventajas de backquote
I
1
2
3
4
´ faciles
´
Hace que las expresiones sean mas
de leer, debido
´ con apostrofo
´
a que la expresion
invertido es similar a su
´ Vean las definiciones de nil!
macro-expansion.
(defmacro nil!
(list ’setq
(defmacro nil!
‘(setq ,var
(var)
var nil))
(var)
nil))
´
Introduccion
Backquote
Definiendo macros
Probando macros
´ complejo: nif
Un ejemplo mas
I
1
2
3
4
´
un if numerico:
CL-USER 8 > (mapcar #’(lambda(x)
(nif x ’p ’c ’n))
’(0 2.5 -8))
(C P N)
Ejemplos
´
Introduccion
Backquote
Definiendo macros
´
Nif con apostrofo
1
2
3
4
5
(defmacro nif (expr pos zero neg)
‘(case (truncate (signum ,expr))
(1 ,pos)
(0 ,zero)
(-1 ,neg)))
Probando macros
Ejemplos
´
Introduccion
Backquote
Definiendo macros
´
Nif sin apostrofo
1
2
3
4
5
6
(defmacro nif (expr pos zero neg)
(list ’case
(list ’truncate (list ’signum expr))
(list 1 pos)
(list 0 zero)
(list -1 neg)))
Probando macros
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Coma-arroba
I
1
2
3
4
5
6
´ que en lugar de
Funciona como la coma normal, solo
´ que antecede, inserta su
insertar el valor de la expresion
´
´ externos:
valor removiendo los parentesis
mas
CL-USER 10 > (setq b ’(1 2 3))
(1 2 3)
CL-USER 11 > ‘(a ,b c)
(A (1 2 3) C)
CL-USER 12 > ‘(a ,@b c)
(A 1 2 3 C)
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplo de uso: mi-when
1
2
3
(defmacro mi-when (test &body body)
’(if ,test
(progn ,@body)))
´
el parametro
&body toma un numero
arbitrario de argumentos
´
´ progn.
y el operador coma-arroba los inserta en un solo
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
Definiendo macros simples
I
1
2
Se comienza con la llamada a la macro que queremos
´
definir. Escribirla en un papel y abajo escriban la expresion
que quieren producir con la macro:
llamada: (mem-eq obj lst)
expansi´
on: (member obj lst :test #’eq)
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
´
Parametros
de la macro
I
1
´
La llamada nos sirve para definir los parametros
de la
macro. En este caso, como necesitamos dos argumentos,
´
el inicio de la macro sera:
(defmacro mem-eq (obj lst)
´
Introduccion
Backquote
Definiendo macros
Probando macros
Cuerpo de la macro
I
I
I
Para cada argumento en la llamada a la macro, tracen un
´
l´ınea hac´ıa donde son insertadas en la expansion.
´
Comiencen el cuerpo de la macro con un apostrofo
invertido.
´ expresion
´ por expresion:
´
Ahora lean la expansion
´ en la llamada,
1. Si no hay una l´ınea conectado la expresion
´ tal cual en el cuerpo de la
entonces escribir la expresion
macro.
´ escriban la expresion
´ precedida por
2. Si hay una conexion,
una coma.
1
2
(defmacro mem-eq (obj lst)
‘(member ,obj ,lst :test #’eq))
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Macros de aridad indeterminada
I
(defmacro while (test &body body)
‘(do ()
((not ,test))
,@body))
1
2
3
4
I
1
2
3
4
5
6
Hay que recurrir a coma-arroba:
y obtenemos una nueva estructura de control
CL-USER> (let ((i 0))
(while (< i 10)
(princ i)
(setf i (1+ i))))
0123456789
NIL
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
Macro expansiones
I
I
´ complejas, es necesario poder observar
Para macros mas
´ ha sido correcta.
si la expansion
Para ello lisp provee dos funciones predefinidas:
I
I
macroexpand muestra como la macro se expandir´ıa antes
de ser evaluada. Si la macro hace uso de otras macros,
´ de la expansion
´ es de poca utilidad.
esta revision
´ macroexpand-1 muestra la expansion
´ de un
La funcion
´ paso.
solo
´
Introduccion
Backquote
Definiendo macros
Probando macros
´
Ejemplos de macro expansion
1
2
3
4
5
6
7
8
9
10
CL-USER 2 > (pprint(macroexpand
’(while (puedas) (rie))))
(BLOCK NIL
(LET ()
(DECLARE (IGNORABLE))
(DECLARE)
(TAGBODY
#:G747 (WHEN (NOT (PUEDAS)) (RETURN-FROM NIL NIL))
(RIE)
(GO #:G747))))
11
12
13
14
CL-USER 3 > (pprint (macroexpand-1
’(while (puedas) (rie))))
(DO () ((NOT (PUEDAS))) (RIE))
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Una meta macro
I
1
2
Si vamos a hacer esto muchas veces, nos convine definir
una macro:
(defmacro mac (expr)
‘(pprint (macroexpand-1 ’,expr)))
de forma que podemos evaluar ahora:
1
2
3
CL-USER> (mac (while (puedas) rie))
(DO () ((NOT (PUEDAS))) RIE)
; Not value
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Ejemplos
´
Evaluando la expansion
I
1
2
3
4
´ obtenida puede evaluarse en el TOP-LEVEL
La expansion
para experimentar con la macro:
CL-USER> (setq aux (macroexpand-1 ’(mem-eq ’a ’(a b c))))
(MEMBER ’A ’(A B C) :TEST #’EQ)
CL-USER> (eval aux)
(A B C)
´
Introduccion
Backquote
Definiendo macros
Probando macros
for, in y random-choice
1
2
3
4
5
6
(defmacro for (var start stop &body body)
(let ((gstop (gensym)))
‘(do ((,var ,start (1+ ,var))
(,gstop ,stop))
((> ,var ,gstop))
,@body)))
7
8
9
10
11
12
(defmacro in (obj &rest choices)
(let ((insym (gensym)))
‘(let ((,insym ,obj))
(or ,@(mapcar #’(lambda(c) ‘(eql ,insym ,c))
choices)))))
13
14
15
16
17
18
19
(defmacro random-choice (&rest exprs)
‘(case (random ,(length exprs))
,@(let ((key -1))
(mapcar #’(lambda(expr)
‘(,(incf key) ,expr))
exprs))))
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Corridas
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
CL-USER 3 > (for x 1 8 (princ x))
12345678
NIL
CL-USER 4 > (in 3 1 2 3)
T
CL-USER 5 > (random-choice 1 2 3)
1
CL-USER 6 > (random-choice 1 2 3)
3
CL-USER 7 > (random-choice 1 2 3)
3
CL-USER 8 > (random-choice 1 2 3)
1
CL-USER 9 > (random-choice 1 2 3)
1
CL-USER 10 > (random-choice 1 2 3)
2
Probando macros
Ejemplos
´
Introduccion
Backquote
Definiendo macros
macro expansiones
1
2
3
4
5
6
7
8
9
10
11
12
13
CL-USER> (mac (for x 1 9 (princ x)))
(DO ((X 1 (1+ X))
(#:G1009 9))
((> X #:G1009)) (PRINC X))
; No value
CL-USER> (mac (in 3 1 2 3))
(LET ((#:G1010 3)) (OR (EQL #:G1010 1)
(EQL #:G1010 2)
(EQL #:G1010 3)))
; No value
CL-USER> (mac (random-choice 1 2 3))
(CASE (RANDOM 3) (0 1) (1 2) (2 3))
; No value
Probando macros
Ejemplos
´
Introduccion
Backquote
Definiendo macros
Probando macros
Bibliograf´ıa
P. Graham.
On Lisp: Advanced Techniques for Common Lisp.
Prentice Hall International, 1993.
Ejemplos