(forma especial)
.MACROnombre_de_macro
:argumento1
:argumento2
...
Una macro es un tipo especial de procedimiento cuyo resultado se evalúa como instrucciones en el contexto del procedimiento que ejecuta la macro. .MACRO es semejante a PARA excepto que el nuevo procedimiento es una macro.
Las macros se usan para crear nuevas estructuras de control comparables a REPITE o SI. Tales estructuras de control no pueden ser duplicados exactamente por procedimientos ordinarios. Por ejemplo, aquí está una definición de REPITE que usa un procedimiento normal:
PARA MI.REPITE :veces :lista_de_instrucciones SI :veces=0 [ALTO] EJECUTA :lista_de_instrucciones MI.REPITE :veces-1 :lista_de_instrucciones FIN
Esta versión parece funcionar bien en la mayoría de usos, por ejemplo:
MI.REPITE 5 [ESCRIBE "hola]
Pero, no funciona si las instrucciones a ejecutar incluyen DEVUELVE, ALTO o LOCAL. Por ejemplo, considera este procedimiento:
PARA EJEMPLO ESCRIBE [Averigua mi palabra secreta en tres intentos.] REPITE 3 [SI LEEPALABRA = "secreta [ESCRIBE "Exacto! ALTO]] ESCRIBE [Lo siento, ¡la palabra es «secreta»!] FIN
Este procedimiento funciona como escrito, pero si utilizas MI.REPITE en lugar de REPITE, no funcionaría porque ALTO detiene MI.REPITE en lugar de EJEMPLO.
La solución es definir MI.REPITE como una macro. En lugar de computar algo por si mismo, una macro debe devolver una lista que contiene instrucciones. Se evalúa el contenido de esta lista como si aparece en el lugar donde se ejecuta la macro. La siguiente macro funciona como REPITE:
.MACRO MI.REPITE :veces :lista_de_instrucciones SI :veces=0 [DEVUELVE []] DEVUELVE FRASE :lista_de_instrucciones (LISTA "MI.REPITE :veces-1 :lista_de_instrucciones) FIN
Cada macro es una operación; todas las macros siempre devuelven algo. Aún en el caso baso, MI.REPITE devuelve la lista de instrucciones vacía.
Para ver como el macro MI.REPITE funciona, veamos el ejemplo.
MI.REPITE 5 [ESCRIBE "hola]
Primero, FMSLogo ejecuta MI.REPITE como procedimiento normal. MI.REPITE devuelve la siguiente lista de instrucciones:
[ESCRIBE "hola MI.REPITE 4 [ESCRIBE "hola]]
Porque MI.REPITE es una macro, FMSLogo inmediatamente ejecuta lo que devuelve. Hacer esto escribe «hola» una vez y ejecuta una otra repetición.
El método demostrado arriba, aunque fácil a entender, es demasiado lento porque cada repetición construye una lista de instrucciones que se ejecuta separadamente. Un método más eficiente es hacer MI.REPITE une macro que funciona como el procedimiento normal excepto cuando las instrucciones a ejecutar incluyen ALTO o DEVUELVE:
.MACRO MI.REPITE :veces :lista_de_instrucciones ATRAPA "repite.etiqueta [ DEVUELVE REPITE.FIN RESULTADOEJECUTA [REPITE1 :veces :lista_de_instrucciones] ] DEVUELVE [] FIN PARA REPITE.FIN :repite.resultado SI VACIO? :repite.resultado [DEVUELVE [ALTO]] DEVUELVE LISTA "DEVUELVE ENTRECOMILLAS PRIMERO :repite.resultado FIN PARA REPITE1 :veces :lista_de_instrucciones SI :veces=0 [LANZA "repite.etiqueta] EJECUTA :lista_de_instrucciones .QUIZADEVUELVE REPITE1 :veces-1 :lista_de_instrucciones FIN
Si las instrucciones no incluyen ALTO o DEVUELVE, entonces REPITE1 llega al caso baso y ejecuta LANZA.
Por lo tanto, la instrucción final de la macro MI.REPITE devuelve una lista vacía, entonces la evaluación de esta lista no hace nada.
Pero, si ocurre un ALTO o DEVUELVE en lista_de_instrucciones
, entonces REPITE.FIN devuelve una instrucción de ALTO o DEVUELVE que se ejecuta en el contexto de donde se ejecuta MI.REPITE.
Los comandos que definen macros tienen nombres que empiezan con un punto (.) porque son herramientas avanzadas de Logo. Es fácil meterte en problemas por definir una macro que no se termina, o por fallar de construir una lista de instrucciones correctamente.
Los usuarios de LISP deben notar que las macros de Logo no son formas especiales. Esto es, los argumentos de una macro se evalúan normalmente, como los de cualquier procedimiento normal. Es solo lo que devuelve una macro que tiene un tratamiento inusual.
Aquí es como puedes crear HAZLOCAL con .MACRO:
.MACRO MI.HAZLOCAL :nombre_de_variable :valor DEVUELVE (LISTA "LOCAL PALABRA "" :nombre_de_variable "APLICA ""HAZ (LISTA :nombre_de_variable :valor) ) FIN
Se la usa de esta manera:
PARA EJEMPLO MI.HAZLOCAL "mivar "hola ESCRIBE :mivar FIN
MI.HAZLOCAL devuelve la lista:
[LOCAL "mivar APLICA "HAZ [mivar hola]]
MI.HAZLOCAL usa APLICA para evitar decidir si el segundo argumento de HAZ requiere ser precedido por una comilla inglesa.
En este caso, sí lo necesita —HAZ "mivar "hola
— pero la comilla inglesa sería incorrecto si el valor fuera una lista.
A menudo, es más conveniente usar la operación ` para construir una lista de instrucciones:
.MACRO MI.HAZLOCAL :nombre_de_variable :valor DEVUELVE `[LOCAL ",:nombre_de_variable APLICA "HAZ [,:nombre_de_variable ,:valor]] FIN
Pero, por otra parte, `
es lento, porque tiene recursión de árbol y es escrito en Logo.