Seguinte: Fichas Acima: Macros Anterior: Macros Úteis
Índice remissivo

Iteradores

Como se pode depreender dos exemplos apresentados, as macros destinam-se essencialmente à criação de açúcar sintático, i.e., de expressões que sejam mais simples de utilizar que outras já existentes. Esta característica torna as macros ferramentas extremamente úteis para criação de tipos abstractos de informação capazes de dar ao utilizador iteradores sobre os objectos desse tipo.

A título de exemplo, vamos considerar a definição de um iterador para os elementos de uma lista. Este iterador deverá ser uma forma especial que recebe um símbolo (uma variável), uma lista e um conjunto de expressões, e itera aquelas expressões com o símbolo ligado a cada elemento da lista. Apresenta-se agora um exemplo da sintaxe da forma especial:

(itera-lista (elem (list 1 2 3 4 5))
  (print elem))

A sua definição é relativamente simples:

(defmacro itera-lista (var-e-lista &rest exprs)
  `(let ((lista ,(cadr var-e-lista))
         (,(car var-e-lista) nil))
     (loop
       (unless lista (return nil))
       (setq ,(car var-e-lista) (car lista)
             lista (cdr lista))
       ,@exprs)))

> (itera-lista (x '(1 2 3)) (print x)) 1 2 3 NIL

Infelizmente, nem tudo está bem. Reparemos no seguinte exemplo:

> (let ((lista '(1 2 3)))
    (itera-lista (x '(4 5 6))
      (print (cons x lista))))
(4 5 6) 
(5 6) 
(6) 
NIL

O problema está no facto de a macro estabelecer uma variável denominada lista, que interfere com a variável exterior do segundo exemplo, pois tem o mesmo nome, ficando assim obscurecida. A referência a lista feita no corpo da macro refere-se assim à variável interna da macro, e não à que seria desejável. Nesta situação diz-se que a macro capturou variáveis.

A solução para este problema está na utilização de variáveis que não possam interferir de modo algum com outras já existentes. Um remédio possível será criar as variáveis necessárias às macros com nomes estranhos, com pouca probabilidade de serem usados pelo utilizador da macro, como por exemplo, %%%$$$lista$$$%%%. No entanto esta solução não é perfeita. O melhor a fazer é usar novos símbolos que não se possam confundir com os já existentes. A função gensym produz um símbolo novo e único de cada vez que é chamada, sendo ideal para resolver estas dificuldades.

Exercício 76

Defina a macro itera-lista usando a referida função gensym para proteger as variáveis do utilizador de uma captura indevida.

Resposta

Exercício 77

Defina de novo a forma especial itera-lista, mas recorrendo desta vez à abordagem da programação funcional, i.e., sem utilizar formas especiais para ciclos nem atribuição de valores a variáveis.

Resposta

Exercício 78

A forma especial caso definida anteriormente sofria do mesmo problema do itera-lista, pois a variável usada para guardar o valor temporário pode obscurecer variáveis idênticas declaradas exteriormente. Redefina a macro de forma a evitar esse perigo.

Resposta


Seguinte: Fichas Acima: Macros Anterior: Macros Úteis
Índice remissivo

Copyright António Leitão, 1995