Seguinte: Funções Acima: Expressões Condicionais Anterior: Selecção Múltipla
Índice remissivo

Formas Especiais

Dada a similitude entre o if e o cond é lícito perguntar se precisaremos dos dois. Será que não é possível definir um em termos do outro ?

A seguinte definição parece ser uma resposta:

> (defun meu-if (condicao consequente alternativa)
    (cond (condicao consequente)
          (t alternativa)))
meu-if
> (meu-if (> 4 3) 5 6)
5
> (meu-if (> 3 4) 5 6)
6

Quando testamos o meu-if, tudo parece bem. No entanto, se escrevermos um exemplo ligeiramente diferente, algo corre mal.

> (defun divide (x y)
    (meu-if (zerop y)
            0
            (/ x y)))
divide
> (divide 2 0)
Error:.....

O que é que está a acontecer? Tentemos seguir as regras do avaliador para explicar a situação.

A avaliação de combinações implica a aplicação da função--que é o primeiro elemento da combinação--aos valores dos restantes elementos. No exemplo (divide 2 0), a aplicação da função divide ao valor de 2 e 0 é o valor da combinação (meu-if (zerop 0) 0 (/ 2 0)), que é a aplicação da função meu-if ao valor de (zerop 0) que é t, 0 que vale 0 e (/ 2 0) que é um erro pois não se pode dividir 2 por 0.

Desta forma, a função meu-if avalia o seu último argumento cedo demais, não esperando pelo valor de (zerop 0) para saber se pode avaliar (/ 2 0).

Mas há situações piores. Consideremos a seguinte definição e aplicação da função factorial.

> (defun meu-fact (n)
    (meu-if (zerop n)
            1
            (* n (meu-fact (- n 1)))))
meu-fact
> (meu-fact 4)
Error:.....

Nesta situação, a avaliação de combinações implica a aplicação da função que é o primeiro elemento da combinação aos valores dos restantes elementos. No exemplo (meu-fact 4), a aplicação da função meu-fact ao valor de 4 é o valor da combinação (meu-if (zerop 4) 1 (* 4 (meu-fact (- 4 1)))), que é a aplicação da função meu-if ao valor de (zerop 4) que é nil , 1 que vale 1 e (meu-fact 3) que é a aplicação da função meu-fact ao valor 3, que é o valor da combinação...

Desta forma, a função meu-if não chega sequer a completar a avaliação dos seus argumentos, não podendo determinar qual dos valores, consequente ou alternativa, retornar, repetindo indefinidamente a aplicação da função meu-fact a argumentos sucessivamente decrescentes.

Suponhamos agora a seguinte interacção com o Lisp:

> (if (> 4 3)
    100
    (inexistente))
100

Segundo o modelo de avaliação que tínhamos apresentado, uma combinação é avaliada aplicando o procedimento que o primeiro elemento da combinação especifica ao valor dos restantes elementos. Nesta óptica, antes de se escolher a opção a seguir, o avaliador deveria avaliar todas elas, i.e., 100 cujo valor é 100 e a aplicação da função inexistente que devia produzir um erro pois a função ainda não foi definida. Porque será que quando testamos isto não é produzido nenhum erro?

É evidente que, de algum modo, o if não seguiu as regras do modelo de avaliação de combinações, caso contrário, teria mesmo produzido um erro. Isto sugere que if não é uma função normal mas sim algo que possui a sua própria regra de avaliação.

Uma forma especial é uma expressão da linguagem que possui a sua própria sintaxe e a sua própria regra de avaliação. É de salientar que uma forma especial não é uma função. Ela faz parte da estrutura do avaliador e não do seu ambiente.

O defun, o if e o cond são algumas das formas especiais. Mas o and e o or também são, pois só avaliam os operandos que forem necessários para determinar o resultado. O and pára de avaliar quando um deles produzir falso, o or quando um deles produzir verdade.

Como uma forma especial possui a sua própria regra de avaliação, nem tudo o que se faz com uma função se pode fazer com uma forma especial, e nem tudo o que se faz com uma forma especial se pode fazer com uma função.

Ao contrário das outras linguagens que possuem muitas formas especiais, Lisp tem muito poucas. Desta forma, a linguagem possui uma regra de avaliação muito simples e muito bem definida, e em que as pequenas excepções são implementadas pelas formas especiais.

No entanto, e ao contrário do que acontece na maioria das outras linguagens, Lisp permite ao utilizador definir novas formas cujo comportamento é semelhante ao das formas especiais. Isso é feito através de macros, que são formas que são transformadas noutras formas (especiais ou não) durante a interpretação ou a compilação.


Seguinte: Funções Acima: Expressões Condicionais Anterior: Selecção Múltipla
Índice remissivo

Copyright António Leitão, 1995