.MACRO

Sinopsis

(forma especial)

.MACRO nombre_de_macro :argumento1 :argumento2 ...
Descripción

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.


SourceForge.net Logo