Sunteți pe pagina 1din 178

Introduo Programao com AutoLisp

Antnio Menezes Leito


Fevereiro 2007

Contedo
1

Programao
1.1 Linguagens de Programao . . . . . . . . . . . . . . . . . . .
1.2 Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1.3 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
10
11
12

Sintaxe, Semntica e Pragmtica


2.1 Sintaxe e Semntica do Lisp . . . . . . . . . . . . . . . . . . .
2.2 O Avaliador . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13
14
14

Elementos da Linguagem
3.1 Elementos primitivos . . . . . . . . .
3.1.1 Nmeros . . . . . . . . . . . .
3.1.2 Cadeias de Caracteres . . . .
3.2 Combinaes . . . . . . . . . . . . .
3.3 Indentao . . . . . . . . . . . . . . .
3.4 Avaliao de Combinaes . . . . . .
3.5 Operadores de Strings . . . . . . . .
3.6 Definio de Funes . . . . . . . . .
3.7 Smbolos . . . . . . . . . . . . . . . .
3.8 Avaliao de Smbolos . . . . . . . .
3.9 Funes de Mltiplos Parmetros . .
3.10 Encadeamento de Funes . . . . . .
3.11 Funes Pr-Definidas . . . . . . . .
3.12 Aritmtica de Inteiros em Auto Lisp
3.13 Aritmtica de Reais em Auto Lisp .
3.14 Smbolos e Avaliao . . . . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

15
16
16
17
18
20
22
23
24
27
28
29
30
30
33
36
38

.
.
.
.
.
.
.
.
.
.

40
40
41
43
44
47
51
54
56
57
59

Combinao de Dados
4.1 Coordenadas . . . . . . . . . . . . . .
4.2 Pares . . . . . . . . . . . . . . . . . .
4.3 Operaes com Coordenadas . . . .
4.4 Abstraco de Dados . . . . . . . . .
4.5 Coordenadas Tri-Dimensionais . . .
4.6 Coordenadas Bi- e Tri-Dimensionais
4.7 A Notao de Lista . . . . . . . . . .
4.8 tomos . . . . . . . . . . . . . . . . .
4.9 Tipos Abstractos . . . . . . . . . . . .
4.10 Coordenadas em AutoCad . . . . . .

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

4.11
4.12
4.13
4.14
4.15
4.16
4.17
4.18
4.19

.
.
.
.
.
.
.
.
.
.
.

60
63
71
72
74
75
79
84
88
88
90

Modelao Tridimensional
5.1 Slidos Tridimensionais Pr-Definidos . . . . . . . . . . . . .
5.2 Modelao de Colunas Dricas . . . . . . . . . . . . . . . . .

92
92
97

Expresses Condicionais
6.1 Expresses Lgicas . . . . . . . . . . . . . . . . .
6.2 Valores Lgicos . . . . . . . . . . . . . . . . . . .
6.3 Predicados . . . . . . . . . . . . . . . . . . . . . .
6.4 Predicados Aritmticos . . . . . . . . . . . . . . .
6.5 Operadores Lgicos . . . . . . . . . . . . . . . . .
6.6 Predicados com nmero varivel de argumentos
6.7 Predicados sobre Cadeias de Caracteres . . . . .
6.8 Predicados sobre Smbolos . . . . . . . . . . . . .
6.9 Predicados sobre Listas . . . . . . . . . . . . . . .
6.10 Reconhecedores . . . . . . . . . . . . . . . . . . .
6.11 Reconhecedores Universais . . . . . . . . . . . .
6.12 Exerccios . . . . . . . . . . . . . . . . . . . . . . .

Coordenadas Polares . . . . . . . . . . . .
A funo command . . . . . . . . . . . . .
Variantes de Comandos . . . . . . . . . .
ngulos em Comandos . . . . . . . . . . .
Efeitos Secundrios . . . . . . . . . . . . .
A Ordem Drica . . . . . . . . . . . . . . .
Parameterizao de Figuras Geomtricas
Documentao . . . . . . . . . . . . . . . .
Depurao . . . . . . . . . . . . . . . . . .
4.19.1 Erros Sintticos . . . . . . . . . . .
4.19.2 Erros Semnticos . . . . . . . . . .

Estruturas de Controle
7.1 Sequenciao . . . . . . . . . . . .
7.2 Invocao de Funes . . . . . . . .
7.3 Variveis Locais . . . . . . . . . . .
7.4 Atribuio . . . . . . . . . . . . . .
7.5 Variveis Globais . . . . . . . . . .
7.6 Variveis Indefinidas . . . . . . . .
7.7 Propores de Vitrvio . . . . . . .
7.8 Seleco . . . . . . . . . . . . . . .
7.9 Seleco MltiplaA Forma cond
2

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.
.
.

99
101
101
101
102
102
103
103
104
104
105
106
107

.
.
.
.
.
.
.
.
.

108
109
110
112
114
116
118
119
122
124

7.10
7.11
7.12
7.13
7.14
7.15

Seleco nas Propores de Vitrvio


Operaes com Coordenadas . . . .
Coordenadas Cilndricas . . . . . . .
Coordenadas Esfricas . . . . . . . .
Recurso . . . . . . . . . . . . . . . .
Depurao de Programas Recursivos
7.15.1 Trace . . . . . . . . . . . . . .
7.16 Templos Dricos . . . . . . . . . . . .
7.17 A Ordem Jnica . . . . . . . . . . . .
7.18 Recurso na Natureza . . . . . . . .

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.
.

126
130
135
138
139
146
147
151
163
171

Programao

A transmisso de conhecimento um dos problemas que desde cedo preocupou a humanidade. Sendo o homem capaz de acumular conhecimento
ao longo de toda a sua vida, com desnimo que enfrenta a ideia de que,
com a morte, todo esse conhecimento se perca.
Para evitar esta perda, a humanidade inventou toda uma srie de mecanismos de transmisso de conhecimento. O primeiro, a transmisso oral,
consiste na transmisso do conhecimento de uma pessoa para um grupo reduzido de outras pessoas, de certa forma transferindo o problema da perda
de conhecimento para a gerao seguinte. O segundo, a transmisso escrita, consiste em registar em documentos o conhecimento que se pretende
transmitir. Esta forma tem a grande vantagem de, por um lado, poder chegar a muitas mais pessoas e, por outro, de reduzir significativamente o
risco de se perder o conhecimento por problemas de transmisso. De facto,
a palavra escrita permite preservar por muito tempo e sem qualquer tipo
de adulterao o conhecimento que o autor pretendeu transmitir.
graas palavra escrita que hoje conseguimos compreender e acumular um vastssimo conjunto de conhecimentos, muitos deles registados h
milhares de anos atrs.
Infelizmente, nem sempre a palavra escrita conseguiu transmitir com
rigor aquilo que o autor pretendia. A lngua natural tem inmeras ambiguidades e evolui substancialmente com o tempo, o que leva a que a interpretao dos textos seja sempre uma tarefa subjectiva. Quer quando escrevemos um texto, quer quando o lemos e o interpretamos, existem omisses,
imprecises, incorreces e ambiguidades que podem tornar a transmisso
de conhecimento falvel. Se o conhecimento que se est a transmitir for
simples, o receptor da informao, em geral, consegue ter a cultura e imaginao suficientes para conseguir ultrapassar os obstculos mas, no caso
da transmisso de conhecimentos mais complexos j isso poder ser muito
mais difcil.
Infelizmente, quando se exige rigor na transmisso de conhecimento,
fazer depender a compreenso desse conhecimento da capacidade de interpretao de quem o recebe pode ter consequncias desastrosas e, de facto,
a histria da humanidade est repleta de acontecimentos catastrficos cuja
causa , to somente, uma insuficiente ou errnea transmisso de conhecimento.
Para evitar estes problemas, inventaram-se linguagens mais rigorosas.
A matemtica, em particular, tem-se obssessivamente preocupado ao longo
dos ltimos milnios com a construo de uma linguagem onde o rigor seja
4

absoluto. Isto permite que a transmisso do conhecimento matemtico seja


muito mais rigorosa que nas outras reas, reduzindo ao mnimo essencial a
capacidade de imaginao necessria de quem est a absorver esse conhecimento.
Para melhor percebermos do que estamos a falar, consideremos um caso
concreto de transmisso de conhecimento, por exemplo, o clculo do factorial de um nmero. Se assumirmos, como ponto de partida, que a pessoa a
quem queremos transmitir esse conhecimento j sabe de antemo o que so
os nmeros e as operaes aritmticas, podemos dizer-lhe que para calcular
o factorial de um nmero qualquer, ter de multiplicar todos os nmeros desde a
unidade at esse nmero. Infelizmente, esta descrio demasiado extensa
e, pior, pouco rigorosa, pois no d ao ouvinte a informao de que os
nmeros que ele tem de multiplicar so apenas os nmeros inteiros. Para
evitar estas imprecises e, simultaneamente, tornar mais compacta a informao a transmitir, a Matemtica inventou todo um conjunto de smbolos
e conceitos cujo significado deve ser compreendido por todos. Por exemplo, para indicar a sequncia de nmeros inteiros entre 1 e 9, a Matemtica
permite-nos escrever 1, 2, 3, . . . , 9. Do mesmo modo, para evitarmos falar
de um nmero qualquer, a Matemtica inventou o conceito de varivel: um
nome que designa qualquer coisa e que pode ser reutilizado em vrias
partes de uma afirmao matemtica com o significado bvio de representar sempre essa mesma coisa. Deste modo, a linguagem Matemtica
permite-nos formular a mesma afirmao sobre o clculo do factorial nos
seguintes termos:
n! = 1 2 3 n
Ser a definio anterior suficientemente rigorosa? Ser possvel interpretla sem necessitar de imaginar a inteno do autor? Aparentemente, sim
mas, na verdade, h um detalhe da definio que exige imaginao: as reticncias. Aquelas reticncias indicam ao leitor que ele ter de imaginar o
que deveria estar no lugar delas e, embora a maioria dos leitores ir imaginar correctamente que o autor pretendia a multiplicao dos sucessores
dos nmeros anteriores, leitores haver cuja imaginao delirante poder
tentar substituir aquelas reticncias por outra coisa qualquer.
Mesmo que excluamos do nosso pblico-alvo as pessoas de imaginao
delirante, h ainda outros problemas com a definio anterior. Pensemos,
por exemplo, no factorial de 2. Qual ser o seu valor? Se substituirmos na
frmula, para n = 2 obtemos:
2! = 1 2 3 2

Neste caso, no s as reticncias deixam de fazer sentido como toda a frmula deixa de fazer sentido, o que mostra que, na verdade, a imaginao
necessria para a interpretao da frmula original no se restringia apenas
s reticncias mas sim a toda a frmula: o nmero de termos a considerar
depende do nmero do qual queremos saber o factorial.
Admitindo que o nosso leitor teria tido a imaginao suficiente para
descobrir esse detalhe, ele conseguiria tranquilamente calcular que 2! =
1 2 = 2. Ainda assim, casos haver que o deixaro significativamente
menos tranquilo. Por exemplo, qual o factorial de zero? A resposta no
parece bvia. E quanto ao factorial de 1? Novamente, no est claro. E
quanto ao factorial de 4.5? Mais uma vez, a frmula nada diz e a nossa
imaginao tambm no consegue adivinhar.
Ser possvel encontrar uma forma de transmitir o conhecimento da
funo factorial que minimize as imprecises, lacunas e ambiguidades?
Ser possvel reduzir ao mnimo razovel a imaginao necessria para
compreender esse conhecimento? Experimentemos a seguinte variante da
definio da funo factorial:
(
1,
se n = 0
n! =
n (n 1)!, se n N.
Teremos agora atingido o rigor suficiente que dispensa imaginao por
parte do leitor? A resposta estar na anlise dos casos que causaram problemas definio anterior. Em primeiro lugar, no existem reticncias, o
que positivo. Em segundo lugar, para o factorial de 2 temos, pela definio:
2! = 2 1! = 2 (1 0!) = 2 (1 1) = 2 1 = 2
ou seja, no h qualquer ambiguidade. Finalmente, vemos que no faz
sentido determinar o factorial de 1 ou de 4.5 pois a definio apresentada
apenas se aplica aos membros de N0 .
Este exemplo mostra que, mesmo na matemtica, h diferentes graus
de rigor nas diferentes formas como se expe o conhecimento. Algumas
dessas formas exigem um pouco mais de imaginao e outras um pouco
menos mas, em geral, qualquer delas tem sido suficiente para que a humanidade tenha conseguido preservar o conhecimento adquirido ao longo da
sua histria.
Acontece que, actualmente, a humanidade conta com um parceiro que
tem dado uma contribuio gigantesca para o seu progresso: o computador. Esta mquina tem a extraordinria capacidade de poder ser instruda
de forma a saber executar um conjunto complexo de tarefas. A actividade
6

da programao consiste, precisamente, da transmisso, a um computador,


do conhecimento necessrio para resolver um determinado problema. Esse
conhecimento denominado um programa. Por serem programveis, os computadores tm sido usados para os mais variados fins e, sobretudo nas ltimas dcadas, tm transformado radicalmente a forma como trabalhamos.
Infelizmente, a extraordinria capacidade de aprendizagem dos computadores vem acompanhada duma igualmente extraordinria incapacidade
de imaginao. O computador no imagina, apenas interpreta rigorosamente o conhecimento que lhe transmitimos sob a forma de um programa.
Uma vez que no tem imaginao, o computador depende criticamente
do modo como lhe apresentamos o conhecimento que queremos transmitir: esse conhecimento tem de estar descrito numa linguagem tal que no
possa haver margem para qualquer ambiguidade, lacuna ou impreciso.
Uma linguagem com estas caractersticas denomina-se, genericamente, de
linguagem de programao.

1.1

Linguagens de Programao

Para que um computador possa resolver um problema necessrio que


consigamos fazer uma descrio do processo de resoluo desse problema
numa linguagem que o computador entenda. Infelizmente, a linguagem
que os computadores entendem de forma inata extraordinariamente
pobre, levando a que a qualquer problema no trivial acabe por exigir uma
exaustiva, entediante e extremamente complexa descrio do processo de
resoluo. As inmeras linguagens de programao que tm sido inventadas visam precisamente aliviar o programador desse fardo, introduzindo
elementos lingusticos capazes de simplificar enormemente essas descries. Por exemplo, os conceitos de funo, matriz, somatrio, ou nmero racional no existem nativamente nos computadores mas muitas linguagens de
programao permitem a sua utilizao de modo a facilitar a descrio de
clculos cientficos. Isto implica, naturalmente, que tem de existir um processo capaz de transformar as descries que o programador faz em descries que o computador entenda. Embora este processo seja relativamente
complexo, o que nos importa saber que ele permite termos linguagens de
programao mais prximas das capacidades do pensamento humano do
que das capacidades de pensamento do computador.
Este ltimo facto tem uma importncia crucial pois permite que passemos a usar linguagens de programao, no s para instruir um computador sobre uma forma de resolver um problema, mas tambm para explicar rigorosamente esse processo a outros seres humanos. A linguagem de
7

programao torna-se assim um meio de transmisso de conhecimento, tal


como a linguagem da matemtica o foi nos ltimos milhares de anos.
Existe uma enorme diversidade de linguagens de programao, umas
mais apetrechadas para a resoluo de um determinado tipo de problemas,
outras mais apetrechadas para a resoluo de outro tipo de problemas. A
escolha de uma linguagem de programao deve estar condicionada, naturalmente, pelo tipo de problemas que queremos resolver, mas no deve ser
um comprometimento total. Para quem programa muito mais importante
compreender os fundamentos e tcnicas da programao do que dominar
esta ou aquela linguagem. No entanto, para mais rigorosamente se explicar
aqueles fundamentos e tcnicas, convm exemplific-los numa linguagem
de programao concreta.
Uma vez que este texto se ir focar na programao aplicada Arquitectura, vamos empregar uma linguagem de programao que esteja particularmente vocacionada para resolver problems geomtricos. Vrias linguagens existem com esta vocao, em geral associadas a ferramentas de desenho assistido por computador. O ArchiCAD, por exemplo, disponibiliza
uma linguagem de programao denominada GDL acrnimo de Geometric Description Languageque permite ao utilizador programar as vrias
formas geomtricas pretendidas. J no caso do AutoCad, a linguagem de
programao empregue o Auto Lisp, um dialecto de uma famosa linguagem de programao denominada Lisp.
Embora a linguagem GDL aparente ser muito diferente da linguagem
Auto Lisp, os conceitos fundamentais so muito semelhantes. sobre estes
conceitos fundamentais da programao que nos iremos debruar embora,
por motivos pedaggicos, seja conveniente particulariz-los numa nica
linguagem. Neste texto iremos explicar os fundamentos da programao
atravs da utilizao da linguagem Auto Lisp, no s pela sua facilidade
de aprendizagem mas tambm pela sua aplicabilidade prtica. No entanto,
uma vez apreendidos esses fundamentos, o leitor dever ser capaz de os
traduzir para qualquer outra linguagem de programao.

1.2

Lisp

Lisp uma linguagem de programao extremamente simples mas com


enorme flexibilidade. Embora tenha sido inicialmente inventada como uma
linguagem matemtica, cedo se procedeu sua implementao em diversos computadores, o que os tornou em executores dos processos descritos
nos programas Lisp.
Desde a sua gnese que a linguagem Lisp prima pela enorme facilidade
8

de extenso. Isto permite ao programador ampliar a linguagem, dotando-a


de novos meios para resolver problemas. Esta capacidade permitiu ao Lisp
sobreviver evoluo da informtica. Embora outras linguagens se tenham
tornado obsoletas, a linguagem Lisp continua activa e a segunda mais
antiga linguagem ainda em utilizao, sendo apenas ultrapassada pela linguagem Fortran.
A facilidade de utilizao, adaptao e extenso da linguagem fez com
que surgissem dezenas de diferentes dialectos: FranzLisp, ZetaLisp, LeLisp, MacLisp, InterLisp, Scheme, T, Nil, XLisp e AutoLISP so apenas alguns dos exemplos mais relevantes. Neste livro iremos debruarmo-nos
sobre o dialecto AutoLISP.
A linguagem AutoLISP derivou da linguagem XLisp e foi incorporada
no AutoCad em 1986, tendo sido intensivamente usada desde ento. Em
1999, o AutoCad passou a disponibilizar uma verso melhorada do AutoLisp denominada Visual LISP que, entre outras diferenas, possui um
compilador para uma execuo mais eficiente e um depurador de programas para facilitar a deteco e correco de erros. A influncia do AutoCad
to grande que vrios outros vendedores decidiram incluir uma implementao de AutoLISP nos seus prprios produtos, de modo a facilitar a
transio de utilizadores.
Um aspecto caracterstico do Lisp ser uma linguagem interactiva. Cada
poro de um programa pode ser desenvolvida, testada e corrigida independentemente das restantes. Deste modo, Lisp permite que o desenvolvimento, teste e correco de programas seja feito de uma forma incremental,
o que facilita muito a tarefa do programador.
Embora os exemplos que iremos apresentar sejam vlidos apenas em
AutoLISP, falaremos genericamente da linguagem Lisp e, apenas quando
necessrio, mencionaremos o AutoLISP.

1.3

Exerccios

Exerccio 1.3.1 A exponenciao bn uma operao entre dois nmeros b e n designados


base e expoente, respectivamente. Quando n um inteiro positivo, a exponenciao definese como uma multiplicao repetida:
bn = b b b
|
{z
}
n

Para um leitor que nunca tenha utilizado o operador de exponenciao, a definio


anterior levanta vrias questes cujas respostas podero no lhe ser evidentes. Quantas
multiplicaes so realmente feitas? Sero n multiplicaes? Sero n 1 multiplicaes? O
que fazer no caso b1 ? E no caso b0 ?
Proponha uma definio matemtica de exponenciao que no levante estas questes.

Exerccio 1.3.2 O que uma linguagem de programao? Para que serve?

Sintaxe, Semntica e Pragmtica

Todas as linguagens possuem sintaxe, semntica e pragmtica.


Em termos muito simples, podemos descrever a sintaxe de uma linguagem como o conjunto de regras que determinam as frases que se podem
construir nessa linguagem. Sem sintaxe, qualquer concatenao arbitrria
de palavras constituiria uma frase. Por exemplo, dadas as palavras Joo,
o, comeu, e bolo, as regras da sintaxe da lngua Portuguesa dizemnos que o Joo comeu o bolo uma frase e que o comeu Joo bolo bolo
no uma frase. Note-se que, de acordo com a sintaxe do Portugus, o
bolo comeu o Joo tambm uma frase sintaticamente correcta.
A sintaxe regula a construo das frases mas nada diz acerca do seu
significado. a semntica de uma linguagem que nos permite atribuir significado s frases da linguagem e que nos permite perceber que a frase o
bolo comeu o Joo no faz sentido.
Finalmente, a pragmtica diz respeito forma usual como se escrevem
as frases da linguagem. Para uma mesma linguagem, a pragmtica varia
de contexto para contexto: a forma como dois amigos ntimos falam entre
si diferente da forma usada por duas pessoas que mal se conhecem.
Estes trs aspectos das linguagens esto tambm presentes quando discutimos linguagens de programao. Contrariamente ao que acontece com
as linguagens naturais que empregamos para comunicarmos uns com os
outros, as linguagens de programao caracterizam-se por serem formais,
obedecendo a um conjunto de regras muito mais simples e rgido que permite que sejam analisadas e processadas mecanicamente.
Neste texto iremos descrever a sintaxe e semntica da linguagem Auto
Lisp. Embora existam formalismos matemticos que permitem descrever
rigorosamente aqueles dois aspectos das linguagens, eles exigem uma sofisticao matemtica que, neste trabalho, desapropriada. Por este motivo, iremos apenas empregar descries informais.
Quanto pragmtica, esta ser explicada medida que formos introduzindo os elementos da linguagem. A linguagem Auto Lisp promove
uma pragmtica que, nalguns aspectos, difere significativamente do que
habitual nos restantes dialectos de Lisp. Uma vez que a pragmtica no influencia a correco dos nossos programas mas apenas o seu estilo, iremos
empregar uma pragmtica ligeiramente diferente da usual em Auto Lisp
mas que pensamos ser mais apropriada.
10

2.1

Sintaxe e Semntica do Lisp

Quando comparada com a grande maioria das outras linguagens de programao, a linguagem Lisp possui uma sintaxe extraordinariamente simples baseada no conceito de expresso.1
Uma expresso, em Lisp, pode ser construda empregando elementos
primitivos como, por exemplo, os nmeros; ou combinando outras expresses entre si como, por exemplo, quando somamos dois nmeros. Como
iremos ver, esta simples definio permite-nos construir expresses de complexidade arbitrria. No entanto, convm relembrarmos que a sintaxe restringe aquilo que podemos escrever: o facto de podermos combinar expresses para produzir outras expresses mais complexas no nos autoriza
a escrever qualquer combinao de subexpresses. As combinaes obedecem a regras sintticas que iremos descrever ao longo do texto.
semelhana do que acontece com a sintaxe, tambm a semntica da
linguagem Lisp , em geral, substancialmente mais simples que a de outras
linguagens de programao. Como iremos ver, a semntica determinada
pelos operadores que as nossas expresses iro empregar. Um operador de
soma, por exemplo, serve para somar dois nmeros. Uma combinao que
junte este operador e, por exemplo, os nmeros 3 e 4 tem, como significado, a soma de 3 com 4, i.e., 7. No caso das linguagens de programao, a
semntica de uma expresso dada pelo computador que a vai avaliar.

2.2

O Avaliador

Em Lisp, qualquer expresso tem um valor. Este conceito de tal modo


importante que todas as implementaes da linguagem Lisp apresentam
um avaliador, i.e., um programa destinado a interagir com o utilizador de
modo a avaliar expresses por este fornecidas.
No caso do AutoCad, o avaliador est contido num ambiente interactivo de desenvolvimento, denominado Visual Lisp e pode ser acedido atravs de menus (menu Tools, submenu AutoLISP, item Visual LISP Editor) ou atravs do comando vlisp. Uma vez acedido o Visual Lisp, o
utilizador encontrar uma janela com o ttulo Visual LISP Console e que
se destina precisamente interaco com o avaliador de Auto Lisp.
Assim, quando o utilizador comea a trabalhar com o Lisp, -lhe apresentado um sinal (denominado prompt) e o Lisp fica espera que o utilizador lhe fornea uma expresso.
1

Originalmente, Lisp dizia-se baseado em S-expression, uma abreviatura para expresses


simblicas. Esta caracterizao ainda aplicvel mas, por agora, no a vamos empregar.

11

_$

O texto _$ a prompt do Lisp, frente da qual vo aparecer as


expresses que o utilizador escrever. O Lisp interacciona com o utilizador
executando um ciclo em que l uma expresso, determina o seu valor e
escreve o resultado. Este ciclo de aces designa-se, tradicionalmente, de
read-eval-print-loop (e abrevia-se para REPL).
Quando o Lisp l uma expresso, constroi internamente um objecto que
a representa. Esse o papel da fase de leitura. Na fase de avaliao, o
objecto construdo analisado de modo a produzir um valor. Esta anlise
feita empregando as regras da linguagem que determinam, para cada
caso, qual o valor do objecto construdo. Finalmente, o valor produzido
apresentado ao utilizador na fase de escrita atravs de uma representao
textual desse valor.
Dada a existncia do read-eval-print-loop, em Lisp no necessrio instruir o computador a escrever explicitamente os resultados de um clculo
como acontece noutras linguages como, por exemplo, em Java. Isto permite que o teste e correco de erros seja bastante facilitada. A vantagem de
Lisp ser uma linguagem interactiva est na rapidez com que se desenvolvem prottipos de programas, escrevendo, testando e corrigindo pequenos
fragmentos de cada vez.
Exerccio 2.2.1 O que o REPL?

Elementos da Linguagem

Em qualquer linguagem de programao precisamos de lidar com duas


espcies de objectos: dados e procedimentos. Os dados so as entidades
que pretendemos manipular. Os procedimentos so descries das regras
para manipular esses dados.
Se considerarmos a linguagem da matemtica, podemos identificar os
nmeros como dados e as operaes algbricas como procedimentos. As
operaes algbricas permitem-nos combinar os nmeros entre si. Por exemplo, 2 2 uma combinao. Uma outra combinao envolvendo mais
dados ser 2 2 2 e, usando ainda mais dados, 2 2 2 2. No entanto, a menos que pretendamos ficar eternamente a resolver problemas de
aritmtica elementar, convm considerar operaes mais elaboradas que
representam padres de clculos. Na sequncia de combinaes que apresentmos evidente que o padro que est a emergir o da operao de
potenciao, i.e, multiplicao sucessiva, tendo esta operao sido definida
12

na matemtica h j muito tempo. A potenciao , portanto, uma abstraco de uma sucesso de multiplicaes.
Tal como a linguagem da matemtica, uma linguagem de programao
deve possuir dados e procedimentos primitivos, deve ser capaz de combinar quer os dados quer os procedimentos para produzir dados e procedimentos mais complexos e deve ser capaz de abstrair padres de clculo de
modo a permitir trat-los como operaes simples, definindo novas operaes que representem esses padres de clculo.
Mais frente iremos ver como possvel definir essas abstraces em
Lisp mas, por agora, vamos debruar-nos sobre os elementos mais bsicos,
os chamados elementos primitivos da linguagem.

3.1

Elementos primitivos

Elementos primitivos so as entidades mais simples com que a linguagem


lida. Os elementos primitivos podem ser divididos em dados primitivos
e procedimentos primitivos. Um nmero, por exemplo, um dado primitivo. J a operao de adio de nmeros um procedimento primitivo.
3.1.1

Nmeros

Como dissemos anteriormente, o Lisp executa um ciclo read-eval-print. Isto


implica que tudo o que escrevemos no Lisp tem de ser avaliado, i.e., tem
de ter um valor, valor esse que o Lisp escreve no cran.
Assim, se dermos um nmero ao avaliador, ele devolve-nos o valor
desse nmero. Quanto vale um nmero? O melhor que podemos dizer
que ele vale por ele prprio. Por exemplo, o nmero 1 vale 1.
_$ 1
1
_$ 12345
12345
_$ 4.5
4.5

Como se v no exemplo, em Lisp, os nmeros podem ser inteiros ou


reais. Os reais so nmeros com um ponto decimal e, no caso do Auto Lisp,
tem de haver pelo menos um dgito antes do ponto decimal:
_$ 0.1
0.1
_$ .1
; error: misplaced dot on input

13

Note-se, no exemplo anterior, que o Auto Lisp produziu um erro. Um


erro uma situao anmala que impede o Auto Lisp de prosseguir o que
estava a fazer. Neste caso, apresentada uma mensagem de erro e o Auto
Lisp volta ao princpio do ciclo read-eval-print.
Exerccio 3.1.1 Descubra qual o maior real e o maior inteiro que o seu Lisp aceita.

Os reais podem ser escritos em notao decimal ou cientfica consoante


o seu tamanho.
3.1.2

Cadeias de Caracteres

As cadeias de caracteres (tambm denominadas strings) so outro tipo de dados primitivo. Um carcter uma letra, um dgito ou qualquer outro smbolo grfico, incluindo os smbolos grficos no visveis como o espao, a
tabulao e outros. Uma cadeia de caracteres especificada atravs de uma
sequncia de caracteres delimitada por aspas. Tal como com os nmeros, o
valor de uma cadeia de caracteres a prpria cadeia de caracteres.
_$ "Ola"
"Ola"
_$ "Eu sou o meu valor"
"Eu sou o meu valor"

Sendo uma string delimitada por aspas fica a dvida sobre como criar
uma string que contm aspas. Para isso, e para outros caracteres especiais,
existe um carcter que o Auto Lisp interpreta de forma distinta: quando,
na criao de uma string, aparece o carcter \ ele assinala que o prximo
carcter tem de ser processado de forma especial. Por exemplo, para se a
criar a string correspondente frase
O Manuel disse Bom dia! ao Pedro.
temos de escrever:
"O Manuel disse \"Bom dia!\" ao Pedro."

O carcter \ denominado um carcter de escape e permite a incluso


em strings de caracteres que, de outra forma, seriam difceis de inserir. A
Tabela 1 mostra as vrias possibilidades.
Para alm dos nmeros e das strings, o Auto Lisp possui ainda outros
tipos de elementos primitivos que iremos explicar mais tarde.

14

Sequncia
\\
\"
\e
\n
\r
\t
\nnn

Resultado
o carcter \ (backslash)
o carcter " (aspas)
o carcter escape
o carcter de mudana de linha (newline)
o carcter de mudana de linha (carriage return)
o carcter de tabulao (tab)
o carcter cujo cdigo octal nnn

Tabela 1: Caracteres de escape vlidos em Auto Lisp

3.2

Combinaes

Uma combinao uma expresso que descreve a aplicao de um operador aos seus operandos. Por exemplo, na matemtica, os nmeros podem
ser combinados usando operaes como a soma ou o produto. Como exemplo de combinaes matemticas, temos 1+2 e 1+23. A soma e o produto
de nmeros so exemplos de operaes extremamente elementares consideradas procedimentos primitivos.
Em Lisp, cria-se uma combinao escrevendo uma sequncia de expresses entre um par de parnteses. Uma expresso um elemento primitivo
ou uma outra combinao. A expresso (+ 1 2) uma combinao dos
elementos primitivos 1 e 2 atravs do procedimento primitivo +. J no
caso (+ 1 (* 2 3)) a combinao entre 1 e (* 2 3), sendo esta ltima
expresso uma outra combinao. Note-se que cada expresso deve ser
separada das restantes por um ou mais espaos. Assim, embora a combinao (* 2 3) possua as trs expresses *, 2 e 3, a combinao (*2 3) s
possui duas*2 e 3sendo que a primeira delas no tem qualquer significado pr-definido.
Por agora, as nicas combinaes com utilidade so aquelas em que as
expresses correspondem a operadores e operandos. Por conveno, o Lisp
considera que o primeiro elemento de uma combinao um operador e os
restantes so os operandos.
A notao que o Lisp utiliza para construir expresses (operador primeiro e operandos a seguir) designada por notao prefixa por o operador ocorrer prviamente aos operandos. Esta notao costuma causar alguma perplexidade a quem inicia o estudo da linguagem, que espera uma
notao mais prxima da que aprendeu em aritmtica e que usada habitualmente nas outras linguagens de programao. Nestas, a expresso
(+ 1 (* 2 3)) usualmente escrita na forma 1 + 2 * 3 (designada no15

tao infixa, por o operador ocorrer entre os operandos) que, normalmente,


mais simples de ler por um ser humano. No entanto, a notao prefixa
usada pelo Lisp tem vantagens sobre a notao infixa:
muito fcil usar operadores que tm um nmero varivel de operandos,2 como por exemplo (+ 1 2 3) ou (+ 1 2 3 4 5 6 7 8 9 10).
Na maioria das outras linguagens de programao apenas existem
operadores unrios ou binrios e necessrio explicitar os operador binrios entre cada dois operandos: 1 + 2 + 3 ou 1 + 2 + 3
+ 4 + 5 + 6 + 7 + 8 + 9 + 10. Se se pretender um operador
ternrio (ou outro) j no se consegue escrever do mesmo modo.
No existe precedncia entre os operadores. Nas linguagens de notao infixa, a expresso 1 + 2 * 3 tem de ser calculada como se
se tivesse escrito 1 + (2 * 3), e no (1 + 2) * 3, i.e., foi criada uma precedncia para eliminar as ambiguidades. Essa precedncia pode ser alterada atravs do emprego de parnteses. Em Lisp,
as expresses seriam necessariamente distintas, (+ 1 (* 2 3)) ou
(* (+ 1 2) 3), no podendo haver qualquer ambiguidade.
Os operadores que ns definimos usam-se exactamente da mesma
maneira que os operadores da linguagem: operador primeiro e operandos a seguir. Na maioria das outras linguagens, os operadores
so infixos, i.e., esto entre os operandos, e os procedimentos por ns
definidos so prefixos, i.e., primeiro vem o nome do procedimento e
depois os operandos. Isto impede a extenso da linguagem de uma
forma coerente.
Para exemplificar este ltimo aspecto, consideremos a operao de
exponenciao numa linguagem com operadores infixos. Para ser coerente com o resto da linguagem, deveria existir um operador, por
exemplo ** que permitisse escrever 3**4 para indicar a quarta potncia de 3. Como, em geral, esse operador no existe, somos obrigados a criar um procedimento que o implemente mas, neste caso,
a sintaxe muda radicalmente pois, em geral, o utilizador no pode
definir novos operadores infixos. A consequncia que o novo operador, por no se poder usar de forma infixa e ter de se usar de forma
prefixa, no fica coerente com as restantes operaes da linguagem,
como a soma e a multiplicao. Em Lisp, pelo contrrio, tanto po2

Estes operadores dizem-se varidicos.

16

demos escrever (* 3 3 3 3) como definir uma funo que permita


escrever (** 3 4).
Para alm das notaes infixa e prefixa existe ainda a notao posfixa
em que o operador se escreve aps os operandos. Esta notao tem as mesmas propriedades da notao prefixa mas , em geral, mais difcil de ler
pelo facto de ser necessrio ler todos os operandos antes de se perceber o
que se vai fazer com eles.

3.3

Indentao

A desvantagem da notao prefixa est na escrita de combinaes complexas. Por exemplo, a expresso 1+2*3-4/5*6 l-se com relativa facilidade
mas, quando escrita na sintaxe do Lisp,(- (+ 1 (* 2 3)) (* (/ 4 5) 6)),
fica com uma forma que, para quem no est ainda habituado, pode ser
mais difcil de perceber devido acumulao de parnteses.
Para tornarmos estas expresses mais fceis de entender, podemos (e
devemos) empregar a tcnica da indentao. Esta tcnica baseia-se no uso
de diferentes alinhamentos na disposio textual dos programas de modo a
facilitar a sua leitura. Assim, ao invs de escrevermos as nossas expresses
todas numa linha ou com arranjos arbitrrios entre linhas, escrevemo-las
ao longo de vrias linhas e com um alinhamento entre linhas que mostre o
relacionamento das vrias subexpresses com a expresso que as contm.
A regra para indentao de combinaes Lisp extremamente simples:
numa linha coloca-se o operador e o primeiro operando; os restantes operandos vm alinhados por debaixo do primeiro, usando-se um nmero suficiente de espaos em branco esquerda para que os operandos fiquem
correctamente arrumados. No caso de uma expresso ser curta, podemos
escrev-la numa s linha, com os operandos logo a seguir ao operador, com
um espao em branco entre cada um. Usando estas duas regras, podemos
reescrever a expresso anterior na seguinte forma, onde usamos linhas verticais imaginrias para salientar a indentao:
(- (+ 1
(* 2 3))
(* (/ 4 5)
6))

Note-se que o facto de se dispor uma combinao ao longo de vrias


linhas em nada afecta a forma como o Lisp a l pois a correcta delimitao
dos elementos da combinao feita apenas pela sua separao visual e
pela utilizao de parnteses correctamente emparelhados.
17

Quando a regra de indentao no suficiente para produzir uma disposio de linhas de texto que seja esteticamente agradvel, usam-se pequenas variaes, como seja colocar o operador numa linha e os operandos
por debaixo, por exemplo:
(um-operador-com-um-nome-muito-grande
1 2 3 4)

No caso geral, podemos ter de empregar as vrias regras em simultneo:


(um-operador (um-operador-com-um-nome-muito-grande
1 2 3 4)
(outro-operador 5
6
(e-ainda-outro 7
8))
(e-o-ultimo-operador 9 10))

Alguns operadores da linguagem possuem uma regra de indentao


prpria. Essas regras sero explicadas medida que formos introduzindo
esses operadores.
A indentao fundamental em Lisp pois muito fcil escrever cdigo
complexo. A grande maioria dos editores preparados para Lisp formatam
automaticamente os programas medida que os escrevemos e mostram o
emparelhamento de parnteses. Desta forma, aps algum tempo de prtica, torna-se muito fcil escrever e ler os programas, por mais complexa
que possa parecer a sua estrutura.
Exerccio 3.3.1 Qual a diferena entre a notao prefixa e a notao infixa?
Exerccio 3.3.2 Em matemtica, usal empregar simultaneamente as diferentes notaes
prefixa, infixa e posfixa. Escreva exemplos de expresses matemticas que utilizem estas
diferentes notaes.
Exerccio 3.3.3 Converta as seguintes expresses da notao infixa da aritmtica para a
notao prefixa do Lisp:
1. 1 + 2 3
2. 1 2 3
3. 1 2 3
4. 1 2 3
5. (1 2) 3
6. (1 2) + 3

18

7. 1 (2 + 3)
8. 2 2 + 3 3 3
Exerccio 3.3.4 Converta as seguintes expresses da notao prefixa do Lisp para a notao
infixa da aritmtica:
1.
2.
3.
4.
5.
6.
7.

(*
(*
(/
(/
(/
((-

(/ 1 2) 3)
1 (- 2 3))
(+ 1 2) 3)
(/ 1 2) 3)
1 (/ 2 3))
(- 1 2) 3)
1 2 3)

Exerccio 3.3.5 Indente a seguinte expresso de modo a ter um nico operando por linha.
(* (+ (/ 3 2) (- (* (/ 5 2) 3) 1) (- 3 2)) 2)

3.4

Avaliao de Combinaes

Como j vimos, o Lisp considera que o primeiro elemento de uma combinao um operador e os restantes so os operandos.
O avaliador determina o valor de uma combinao como o resultado de
aplicar o procedimento especificado pelo operador ao valor dos operandos.
O valor de cada operando designado de argumento do procedimento.
Assim, o valor da combinao (+ 1 (* 2 3)) o resultado de somar o
valor de 1 com o valor de (* 2 3). Como j se viu, 1 vale 1 e (* 2 3)
uma combinao cujo valor o resultado de multiplicar o valor de 2 pelo
valor de 3, o que d 6. Finalmente, somando 1 a 6 obtemos 7.
_$ (* 2 3)
6
_$ (+ 1 (* 2 3))
7

Uma diferena importante entre o Auto Lisp e a aritmtica ocorre na


operao de diviso. Matematicamente falando, 7/2 uma fraco, i.e.,
no um nmero inteiro. Infelizmente, o Auto Lisp no sabe lidar com
fraces e, consequentemente, emprega uma definio ligeiramente diferente para o operador de diviso: em Auto Lisp, o smbolo / representa
a diviso inteira, i.e., o nmero que multiplicado pelo divisor e somado ao
resto iguala o dividendo. Assim, (/ 7 2) avalia para 3. No entanto, quando
est a lidar com nmeros reais, o Auto Lisp j faz a diviso segundo as
regras da matemtica mas, possivelmente, envolvendo perdas de preciso.
Assim, (/ 7.0 2) produz 3.5.
19

Exerccio 3.4.1 Calcule o valor das seguintes expresses Lisp:


1. (* (/ 1 2) 3)
2. (* 1 (- 2 3))
3. (/ (+ 1 2) 3)
4. (- (- 1 2) 3)
5. (- 1 2 3)
6. (- 1)

3.5

Operadores de Strings

Para alm dos operadores que j vimos para nmeros, existem operadores
para strings. Por exemplo, para concatenar vrias strings, existe o operador
strcat. A concatenao de vrias strings produz uma s string com todos
os caracteres dessas vrias strings e pela mesma ordem:
_$ (strcat "1" "2")
"12"
_$ (strcat "um" "dois" "tres" "quatro")
"umdoistresquatro"
_$ (strcat "eu" " " "sou" " " "uma" " " "string")
"eu sou uma string"
_$ (strcat "E eu" " sou " "outra")
"E eu sou outra"

Para saber o nmero de caracteres de uma string existe o operador strlen:


_$ (strlen (strcat "eu" " " "sou" " " "uma" " " "string"))
17
_$ (strlen "")
0

Note-se que as aspas so os delimitadores de strings e no contam como


caracteres.
A funo substr providencia uma terceira operao til com strings:
ela permite obter parte de uma string. A parte a obter especificada atravs
da posio do primeiro carcter e do nmero de caracteres a considerar.
Numa string, a posio de um carcter tambm denominada de ndice
do caracter. O Auto Lisp considera que o primeiro carcter de uma string
tem o ndice 1. A funo substr recebe, como argumentos, uma string,
um ndice e, opcionalmente, um nmero de caracteres e devolve a parte
da string que comea no ndice dado e que tem o nmero de caracteres
dado no parmetro opcional ou todos os restantes caracteres no caso de
esse nmero no ter sido dado. Assim, temos:
20

_$ (substr "abcd" 1)
"abcd"
_$ (substr "abcd" 2)
"bcd"
_$ (substr "abcd" 2 2)
"bc"
Exerccio 3.5.1 Qual o resultado das seguintes avaliaes?
1. (strcat "a" "vista" "da" " baixa" "da" " banheira")
2. (substr (strcat "Bom dia") 1 3)
3. (substr (strcat "Bom dia") 5)

3.6

Definio de Funes

Para alm das operaes bsicas aritmticas, a matemtica disponibilizanos um vastssimo conjunto de outras operaes que se definem custa
daquelas. Por exemplo, o quadrado de um nmero uma operao (tambm
designada por funo) que, dado um nmero, produz o resultado de multiplicar esse nmero por ele prprio. Matematicamente falando, define-se o
quadrado de um nmero pela funo x2 = x x.
Tal como em matemtica, pode-se definir numa linguagem de programao a funo que obtm o quadrado de um nmero. Em Lisp, para
obtermos o quadrado de um nmero qualquer, por exemplo, 5, escrevemos a combinao (* 5 5). No caso geral, dado um nmero qualquer x,
sabemos que obtemos o seu quadrado escrevendo (* x x). Falta apenas
associar um nome que indique que, dado um nmero x, obtemos o seu quadrado avaliando (* x x). Lisp permite-nos fazer isso atravs da operao
defun (abreviatura de define function):
_$ (defun quadrado (x) (* x x))
QUADRADO

Note-se que o Auto Lisp, ao avaliar a definio da funo quadrado


devolve como valor o nome da funo definida. Note-se ainda que o Auto
Lisp escreve esse nome em maisculas. Na realidade, o que se passa que,
por motivos histricos, todos os nomes que escrevemos no Auto Lisp so
traduzidos para maisculas assim que so lidos. Por este motivo, quadrado,
QUADRADO, Quadrado, qUaDrAdo, ou qualquer outra combinao de maisculas e minsculas designa o mesmo nome em Auto Lisp: QUADRADO. Apesar da converso para maisculas que feita na leitura, pragmtica usual
escrevermos os nossos programas em letras minsculas.
21

Como se pode ver pela definio da funo quadrado, para se definirem novos procedimentos em Lisp, necessrio criar uma combinao
de quatro elementos. O primeiro elemento desta combinao a palavra
defun, que informa o avaliador que estamos a definir uma funo. O segundo elemento o nome da funo que queremos definir, o terceiro elemento uma combinao com os parmetros da funo e o quarto elemento
a expresso que determina o valor da funo para aqueles parmetros. De
modo genrico podemos indicar que a definio de funes feita usando
a seguinte forma:
(defun nome (parmetro1 ... parmetron )
corpo)

Quando se d uma expresso desta forma ao avaliador, ele acrescenta


a funo ao conjunto de funes da linguagem, associando-a ao nome que
lhe demos e devolve como valor da definio o nome da funo definida.
Se j existir uma funo com o mesmo nome, a definio anterior simplesmente esquecida. Os parmetros de um procedimento so designados
parmetros formais e so os nomes usados no corpo de expresses para nos
referirmos aos argumentos correspondentes. Quando escrevemos no avaliador de Lisp (quadrado 5), 5 o argumento do procedimento. Durante o
clculo da funo este argumento est associado ao parmetro formal x. Os
argumentos de uma funo so tambm designados por parmetros actuais.
No caso da funo quadrado, a sua definio diz que para se determinar o quadrado de um nmero x, devemos multiplicar esse nmero por ele
prprio (* x x). Esta definio associa a palavra quadrado a um procedimento, i.e., a uma descrio do modo de produzir o resultado pretendido.
Note-se que este procedimento possui parmetros, permitindo o seu uso
com diferentes argumentos. Para se utilizar este procedimento, podemos
avaliar as seguinte expresses:
_$ (quadrado 5)
25
_$ (quadrado 6)
36

A regra de avaliao de combinaes que descrevemos anteriormente


tambm vlida para as funes por ns definidas. Assim, a avaliao da expresso (quadrado (+ 1 2)) passa pela avaliao do operando (+ 1 2).
Este operando tem como valor 3, valor esse que usado pela funo no lugar do parmetro x. O corpo da funo ento avaliado mas substituindo

22

todas as ocorrncias de x pelo valor 3, i.e., o valor final ser o da combinao (* 3 3).
Para se perceber a avaliao de combinaes que o Lisp emprega, til
decompor essa avaliao nas suas etapas mais elementares. No seguinte
exemplo mostramos o processo de avaliao para a expresso
(quadrado (quadrado (+ 1 2)))

Em cada passo, uma das sub-expresses foi avaliada.


(quadrado (quadrado (+ 1 2)))

(quadrado (quadrado 3))

(quadrado (* 3 3))

(quadrado 9)

(* 9 9)

81
Como dissemos, a definio de funes permite-nos associar um procedimento a um nome. Isto implica que o Lisp tem de possuir uma memria
onde possa guardar a funo e a sua associao ao nome dado. Esta memria do Lisp designa-se ambiente.
Note-se que este ambiente apenas existe enquanto estamos a trabalhar
com a linguagem. Quando terminamos, perde-se todo o ambiente. Para
evitar perdermos as definies de procedimentos que tenhamos feito, convm regist-las num suporte persistente, como seja um ficheiro. Para facilitar a utilizao, o Lisp permite que se avaliem as diversas definies a partir de um ficheiro. Por este motivo, o processo usual de trabalho com Lisp
consiste em escrever as vrias definies em ficheiros embora se continue
a usar o avaliador de Lisp para experimentar e testar o correcto funcionamento das nossas definies.
Exerccio 3.6.1 Defina a funo dobro que, dado um nmero, calcula o seu dobro.

23

3.7

Smbolos

A definio de funes em Lisp passa pela utilizao de nomes: nomes para


as funes e nomes para os parmetros das funes.
Internamente, o Lisp representa os nomes atravs de smbolos. Cada
nome diferente que lido pelo Lisp acaba por ter um smbolo associado
e este smbolo que usado para determinar o significado do nome, por
exemplo, qual a funo ou qual o parmetro a que ele diz respeito.
Em Lisp, quase no existem limitaes para a escrita de nomes. Um
nome como quadrado to vlido como + ou como 1+2*3 pois o que
separa um nome dos outros elementos de uma combinao so apenas parnteses e espao em branco.3
No caso do Auto Lisp, os nicos caracteres que no se podem utilizar
nos nomes dos smbolos so o abrir e fechar parnteses ( e ), a plica (tambm chamada apstrofo) , as aspas ", o ponto . e o ponto e vrgula ;. Todos os restantes caracteres se podem usar para nomes de funes mas, na
prtica, a criao de nomes segue algumas regras que convm ter presente:
Apenas se devem usar as letras do alfabeto, os dgitos, os smbolos
aritmticos e alguns caracteres de pontuao, como o ponto, o ponto
de exclamao e o ponto de interrogao. Por motivos de portabilidade, convm evitar o uso de caracteres acentuados.
Se o nome da funo composto por vrias palavras, deve-se separar
as palavras com traos (-). Por exemplo, uma funo que calcula a
rea de um crculo poder ter como nome o smbolo area-circulo.
J os nomes areacirculo, area_circulo e area+circulo sero
menos apropriados.
Se o nome corresponde a uma interrogao, deve-se terminar o nome
com um ponto de interrogao (?) ou, na tradio Lisp, com a letra
p.4 Por exemplo, uma funo que indica se um nmero par poder
ter o nome par?.
Se a funo faz uma converso entre dois tipos de valores, o nome
poder ser obtido a partir dos nomes dos tipos com uma seta entre
eles a indicar a direco da converso. Por exemplo, uma funo que
3
Dentro do conceito de espao em branco consideram-se os caracteres de espao, de
tabulao e de mudana de linha.
4
Esta letra a abreviatura de predicado, um tipo de funo que iremos estudar mais
frente.

24

converte euros para libras poder ter como nome euros->libras


ou, ainda melhor, libras<-euros.
Exerccio 3.7.1 Indique um nome apropriado para cada uma das seguintes funes:
1. Funo que calcula o volume de uma esfera.
2. Funo que indica se um nmero primo.
3. Funo que converte uma medida em centmetros para polegadas.

3.8

Avaliao de Smbolos

Vimos que todos os outros elementos primitivos que apresentmos at agora,


nomeadamente, os nmeros e as cadeias de caracteres avaliavam para eles
prprios, i.e., o valor de uma expresso composta apenas por um elemento
primitivo o prprio elemento primitivo. No caso dos smbolos, isso j no
verdade.
Lisp atribui um significado muito especial aos smbolos. Reparemos
que, quando definimos uma funo, o nome da funo um smbolo. Os
parmetros formais da funo tambm so smbolos. Quando escrevemos
uma combinao, o avaliador de Lisp usa a definio de funo que foi
associada ao smbolo que constitui o primeiro elemento da combinao.
Isto quer dizer que o valor do primeiro smbolo de uma combinao a
funo que lhe est associada. Admitindo que tnhamos definido a funo
quadrado tal como vimos na seco 3.6, podemos verificar este comportamento experimentando as seguintes expresses:
_$ (quadrado 3)
9
_$ quadrado
#<USUBR @1d4c66f4 QUADRADO>

Como se pode ver pelo exemplo anterior, o valor do smbolo quadrado


uma entidade que o Lisp descreve usando uma notao especial. A entidade em questo , como vimos, uma funo. O mesmo comportamento
ocorre para qualquer outra funo pr-definida na linguagem:5
_$ +
#<SUBR @0a87c5a8 +>
_$ *
#<SUBR @0a87c588 *>
5

Um olhar mais atento encontrar uma pequena diferena: no caso do quadrado, a


funo associada do tipo USUBR enquanto que nos casos do + e * as funes associadas
so do tipo SUBR. A diferena entre estes dois tipos est relacionada com o facto de as
SUBRs estarem compiladas, permitindo a sua invocao de forma mais eficiente. O termo
SUBR uma abreviatura de subroutine.

25

Como vimos com o + e o *, alguns dos smbolos esto pr-definidos na


linguagem. Por exemplo, o smbolo PI tambm existe pr-definido com
uma aproximao do valor de .
_$ pi
3.141519

No entanto, quando o avaliador est a avaliar o corpo de uma funo,


o valor de um smbolo especificado nos parmetros da funo o argumento correspondente na invocao da funo. Assim, na combinao
(quadrado 3), depois de o avaliador saber que o valor de quadrado
a funo por ns definida atrs e que o valor de 3 3, o avaliador passa
a avaliar o corpo da funo quadrado mas assumindo que, durante essa
avaliao, o nome x, sempre que for necessrio, ir ter como valor precisamente o mesmo 3 que foi associado ao parmetro x.
Exerccio 3.8.1 Defina a funo radianos<-graus que recebe uma quantidade angular
em graus e calcula o valor correspondente em radianos. Note que 180 graus correspondem
a radianos.
Exerccio 3.8.2 Defina a funo graus<-radianos que recebe uma quantidade angular
em radianos e calcula o valor correspondente em graus.
Exerccio 3.8.3 Defina a funo que calcula o permetro de uma circunferncia de raio r.

3.9

Funes de Mltiplos Parmetros

J vimos e definimos vrias funes em Lisp. At agora, todas as que definimos tinham um nico parmetro mas, obviamente, possvel definir
funes com mais do que um parmetro.
Por exemplo, a rea A de um tringulo de base b e altura c define-se
matematicamente por A(b, c) = bc
2 . Em Lisp, teremos:
(defun A (b c) (/ (* b c) 2))

Como se pode ver, a definio da funo em Lisp idntica definio


correspondente em Matemtica, com a diferena de os operadores se usarem de forma prefixa e o smbolo de definio matemtica = se escrever
defun. No entanto, contrariamente ao que usual ocorrer em Matemtica, os nomes que empregamos em Lisp devem ter um significado claro.
Assim, ao invs de se escrever A prefervel escrever area-triangulo.
Do mesmo modo, ao invs de se escrever b e c, seria prefervel escrever
base e altura. Tendo estes aspectos em conta, podemos apresentar uma
definio mais legvel:
26

(defun area-triangulo (base altura)


(/ (* base altura) 2))

Quando o nmero de definies aumenta torna-se particularmente importante para quem as l que se perceba rapidamente o seu significado e,
por isso, de crucial importncia que se faa uma boa escolha de nomes.
Exerccio 3.9.1 Defina uma funo que calcula o volume de um paralelippedo a partir do
seu comprimento, altura e largura. Empregue nomes suficientemente claros.
Exerccio 3.9.2 Defina a funo media que calcula o valor mdio entre dois outros valores.
Por exemplo (media 2 3)2.5.

3.10

Encadeamento de Funes

Todas funes por ns definidas so consideradas pelo avaliador de Lisp


em p de igualdade com todas as outras definies. Isto permite que elas
possam ser usadas para definir ainda outras funes. Por exemplo, aps
termos definido a funo quadrado, podemos definir a funo que calcula
a rea de um crculo de raio r atravs da frmula r2 .
(defun area-circulo (raio)
(* pi (quadrado raio)))

Durante a avaliao de uma expresso destinada a computar a rea de


um crculo, a funo quadrado acabar por ser invocada. Isso visvel na
seguinte sequncia de passos de avaliao:
(area-circulo 2)
(* pi (quadrado 2))
(* 3.141519 (quadrado 2))
(* 3.141519 (* 2 2))
(* 3.141519 4)
12.566076
Exerccio 3.10.1 Defina a funo que calcula o volume de um cilindro com um determinado raio e comprimento. Esse volume corresponde ao produto da rea da base pelo comprimento do cilindro.

3.11

Funes Pr-Definidas

A possibilidade de se definirem novas funes fundamental para aumentarmos a flexibilidade da linguagem e a sua capacidade de se adaptar aos
problemas que pretendemos resolver. As novas funes, contudo, precisam
27

de ser definidas custa de outras que, ou foram tambm por ns definidas


ou, no limite, j estavam pr-definidas na linguagem.
Isto mesmo se verifica no caso da funo area-circulo que definimos acima: ela est definida custa da funo quadrado (que foi tambm
por ns definida) e custa da operao de multiplicao. No caso da funo quadrado, ela foi definida com base na operao de multiplicao. A
operao de multiplicao que, em ltima anlise, a base do funcionamento da funo area-circulo , na realidade, uma funo pr-definida
do Lisp.
Como iremos ver, Lisp providencia um conjunto razoavelmente grande
de funes pr-definidas. Em muitos casos, elas so suficientes para o que
pretendemos mas, em geral, no nos devemos coibir de definir novas funes sempre que acharmos necessrio.
A Tabela 2 apresenta uma seleco de funes matemticas pr-definidas
do Auto Lisp. Note-se que, devido s limitaes sintticas do Auto Lisp (e
que so comuns a todas as outras linguagens de programao), h vrios
casos em que uma funo em Auto Lisp emprega uma notao diferente
daquela que usual em matemtica. Por exemplo, a funo raiz quadrada

x escreve-se como (sqrt x). O nome sqrt uma contraco do Ingls square root e contraces semelhantes so empregues para vrias outras
funes. Por exemplo, a funo valor absoluto |x| escreve-se (abs x) (de
absolute value), a funo parte inteira bxc escreve-se (fix x) (de fixed point)
e a funo potncia xy que se escreve (expt x y) (de exponential). A Tabela 3 mostra as equivalncias mais relevantes entre invocaes de funes
em Auto Lisp e as correspondentes invocaes na Matemtica.
A Tabela 4 apresenta uma seleco de funes sobre strings pr-definidas
do Auto Lisp. medida que formos apresentando novos tpicos do Auto
Lisp iremos explicando outras funes pr-definidas que sejam relevantes
para o assunto.
Exerccio 3.11.1 Como pode verificar pelas Tabelas 2 e 3, em Auto Lisp, uma multiplicao
sem argumentos produz o valor zero, tal como acontece no caso de uma diviso sem argumentos. No entanto, os matemticos contestam este resultado, afirmando que est errado
e que o valor correcto deveria ser outro. Concorda? Em caso afirmativo, qual deveria ser o
valor correcto?
Exerccio 3.11.2 Embora o seno (sin) e o cosseno (cos) sejam funes pr-definidas em
sin x
Auto Lisp, a tangente (tan) no . Defina-a a partir da frmula tan x = cos
.
x
Exerccio 3.11.3 Do conjunto das funes trigonomtricas inversas, o Auto Lisp apenas providencia o arco tangente (atan). Defina tambm as funes arco-seno (asin) e arco-cosseno

28

(acos), cujas definies matemticas so:


x
asin x = atan
1 x2

1 x2
acos x = atan
x
Exerccio 3.11.4 A funo fix permite truncar um nmero real, produzindo o nmero inteiro que se obtm pela eliminao da parte fraccionria desse real. Defina a funo round
que permite arredondar um nmero real, i.e., produzir o inteiro que mais prximo desse
nmero real.
Exerccio 3.11.5 Para alm da funo round que arredonda para o inteiro mais prximo
e da funo fix que arredonda para baixo, usual considerar ainda a funo ceiling
que arrendonda para cima. Defina esta funo.
Exerccio 3.11.6 Traduza as seguintes expresses matemticas para Auto Lisp:
q
1
1.
log 2|(39 log 25)|
2.
3.

cos4 2

atan 3
1
2

3 + sin 2 2

Exerccio 3.11.7 Traduza as seguintes expresses Auto Lisp para a notao matemtica:
1. (log (sin (+ (expt 2 4) (/ (fix (atan pi)) (sqrt 5)))))
2. (expt (cos (cos (cos 0.5))) 5)
3. (sin (/ (cos (/ (sin (/ pi 3)) 3)) 3))
Exerccio 3.11.8 Defina o predicado impar? que, dado um nmero, testa se ele mpar,
i.e., se o resto da diviso desse nmero por dois um. Para calcular o resto da diviso de
um nmero por outro, utilize a funo pr-definida rem.
Exerccio 3.11.9 Em vrias actividades usual empregarem-se expresses padronizadas.
Por exemplo, se o aluno Passos Dias Aguiar pretende obter uma certido de matrcula da
universidade que frequenta, ter de escrever uma frase da forma:
Passos Dias Aguiar vem respeitosamente pedir a V. Exa. que se digne
passar uma certido de matrcula.
Por outro lado, se a aluna Maria Gustava dos Anjos pretende um certificado de habilitaes, dever escrever:
Maria Gustava dos Anjos vem respeitosamente pedir a V. Exa. que se
digne passar um certificado de habilitaes.

29

Para simplificar a vida destas pessoas, pretende-se que implemente uma funo denominada requerimento que, convenientemente parameterizada, gera uma string com a
frase apropriada para qualquer um dos fins anteriormente exemplificados e tambm para
outros do mesmo gnero que possam vir a ser necessrios. Teste a sua funo para garantir
que consegue reproduzir os dois exemplos anteriores passando o mnimo de informao
nos argumentos da funo.
Exerccio 3.11.10 Tambm na actividade da Arquitectura, usual os projectos necessitarem de informaes textuais escritas numa forma padronizada. Por exemplo, considere as
seguintes trs frases contidas nos Cadernos de Encargos de trs diferentes projectos:
Toda a caixilharia ser metlica, de alumnio anodizado, cor natural, construda com os perfis utilizados pela Serralharia do Corvo (ou semelhante),
sujeitos a aprovao da Fiscalizao.
Toda a caixilharia ser metlica, de alumnio lacado, cor branca, construda com os perfis utilizados pela Technal (ou semelhante), sujeitos a aprovao da Fiscalizao.
Toda a caixilharia ser metlica, de ao zincado, preparado com primrios de aderncia, primrio epoxdico de proteco e acabamento com esmalte
SMP, cor 1050-B90G (NCS), construda com os perfis standard existentes no
mercado, sujeitos a aprovao da Fiscalizao.
Defina uma funo que, convenientemente parameterizada, gera a string adequada
para os trs projectos anteriores, tendo o cuidado de a generalizar para qualquer outra
situao do mesmo gnero.

3.12

Aritmtica de Inteiros em Auto Lisp

Vimos que o Auto Lisp disponibiliza, para alm dos operadores aritmticos
usuais, um conjunto de funes matemticas. No entanto, h que ter em
conta que existem diferenas substanciais entre o significado matemtico
destas operaes e a sua implementao no Auto Lisp.
Um primeiro problema importante tem a ver com a gama de inteiros.
Em Auto Lisp, os inteiros so representados com apenas 32 bits de informao.6 Isto implica que apenas se conseguem representar inteiros desde
2147483647 at 2147483647. Para tornar a situao ainda mais problemtica, embora exista esta limitao, quando ela no respeitada o Auto Lisp
no emite qualquer aviso. Este comportamento usual na maioria das linguagens de programao e est relacionado com questes de performance.7
6

Em AutoCad, a gama de inteiros ainda mais pequena pois a sua representao apenas
usa 16 bits, permitindo apenas representar os inteiros de 32768 a 32767. Isto no afecta o
Auto Lisp excepto quando este tem de passar inteiros para o AutoCad.
7
Curiosamente, a maioria dos dialectos de Lisp (mas no o Auto Lisp) no apresenta este
comportamento, preferindo representar nmeros inteiros com dimenso to grande quanto
for necessrio.

30


2147483648

+
2 1 0 +1 +2

1 0 +1

2147483648

+2147483647

+2

+2147483647

Figura 1: A recta infinita dos inteiros empregue em aritmtica e o crculo


dos inteiros modulares empregues em Auto Lisp.
Infelizmente, a performance tem como preo o poder originar resultados
aparentemente bizarros:
_$ (+ 2147483647 1)
-2147483648

Este resultado surge porque no Auto Lisp, na realidade, as operaes


aritmticas com inteiros so modulares. Isto implica que a sequncia dos inteiros no corresponde a uma linha infinita nas duas direco, antes correspondendo a um crculo em que a seguir ao maior nmero inteiro positivo
aparece o menor nmero inteiro negativo. Este comportamento encontrase explicado na Figura 1.
Um segundo problema que j referimos anteriormente est no facto de
o Auto Lisp no saber representar fraces. Isto implica que uma diviso
de dois inteiros no corresponde diviso matemtica mas sim a uma operao substancialmente diferente denominada diviso inteira: uma diviso
em que a parte fraccional do resultado eliminada, i.e., apenas se considera
o quociente da diviso, eliminando-se o resto. Assim, (/ 7 2) 3 e no 7/2
ou 3.5. Obviamente, isto inviabiliza algumas equivalncias matemticas
bvias. Por exemplo, embora 31 3 = 1, em Auto Lisp temos:
_$ (* (/ 1 3) 3)
0

Finalmente, um terceiro problema que ocorre com os inteiros que a


leitura de um nmero inteiro que excede a gama dos inteiros implica a sua
31

converso automatica para o tipo real.8 Neste caso, o avaliador do Auto


Lisp nunca chega a ver o nmero inteiro pois ele foi logo convertido
para real na leitura. Esse comportamento visvel no seguinte exemplo:
_$ 1234567890
1234567890
_$ 12345678901
1.23457e+010

Tambm este aspecto do Auto Lisp pode ser fonte de comportamentos


bizarros, tal como demonstrado pelo seguinte exemplo:
_$ (+ 2147483646 1)
2147483647
_$ (+ 2147483647 1)
-2147483648
_$ (+ 2147483648 1)
2.14748e+009

importante salientar que a converso de inteiro para real que visvel


na ltima interaco feita logo na leitura.
Exerccio 3.12.1 Calcule o valor das seguintes expresses Auto Lisp:
1. (- 1)
2. (- 1 1)
3. (- 1 1 1)
4. (/ 1 1)
5. (/ 1 2)
6. (/ (* 3 2) 2)
7. (* (/ 3 2) 2)
8. (/ (/ (/ 8 2) 2) 2)
9. (/ 8 (/ 2 (/ 2 2)))
10. (+ (/ 1 2) (/ 1 2))
Exerccio 3.12.2 Explique o seguinte comportamento do Auto Lisp:9
8

Note-se que estamos apenas a falar da leitura de nmeros. Como j vimos, as operaes
aritmticas usuais no apresentam este comportamento.
9
Este exemplo mostra que, em Auto Lisp, h um inteiro que no pode ser lido mas que
pode ser produzido como resultado de operaes aritmticas. importante salientar que
este comportamento verdadeiramente bizarro exclusivo do Auto Lisp e no se verifica em
mais nenhum dialecto de Lisp.

32

_$ (- -2147483647 1)
-2147483648
_$ (- -2147483648 1)
-2.14748e+009
_$ (- -2147483647 2)
2147483647

Exerccio 3.12.3 A rea A de um pentgono regular inscrito num crculo de raio r dada
pela frmula
q

5
A = r2 10 + 2 5
8
Defina uma funo Auto Lisp que calcule essa rea.
Exerccio 3.12.4 Defina uma funo que calcula o volume de um elipside de semi-eixos a,
b e c. Esse volume pode ser obtido pela frmula V = 34 abc.

3.13

Aritmtica de Reais em Auto Lisp

Em relao aos reais, o seu comportamento mais prximo do que se considera matematicamente correcto mas, ainda assim, h vrios problemas
com que preciso lidar.
A gama dos reais vai desde 4.94066 10324 at 1.79769 10+308 . Se o
Auto Lisp tentar ler um nmero real que excede esta gama ele imediatamente convertido para um nmero especial que representa o infinito:
_$ 2e400
1.#INF
_$ -2e400
-1.#INF

Note-se que 1.#INF ou -1.#INF a forma do Auto Lisp indicar um


valor que excede a capacidade de representao de reais do computador.
No infinito, como se poderia pensar, mas apenas um valor excessivamente grande para as capacidades do Auto Lisp. Do mesmo modo, quando
alguma operao aritmtica produz um nmero que excede a gama dos reais, simplesmente gerada a representao do infinito.
_$ 1e300
1.0e+300
_$ 1e100
1.0e+100
_$ (* 1e300 1e100)
1.#INF

Nestes casos, em que o resultado de uma operao um nmero que


excede as capacidades da mquina, dizemos que ocorreu um overflow.
33

As operaes com nmeros reais tm a vantagem de a maioria dos computadores actuais conseguirem detectar o overflow de reais e reagirem em
conformidade (gerando um erro ou simplesmente produzindo uma representao do infinito). Como vimos anteriormente, se se tivessem usado
nmeros inteiros, ento o overflow no seria sequer detectado, produzindo
comportamentos aparentemente bizarros, como, por exemplo, o produto
de dois nmeros positivos ser um nmero negativo.
H ainda dois outros problemas importantes relacionados com reais:
erros de arredondamento e reduo de preciso na escrita. A ttulo de
exemplo, consideremos a bvia igualdade matemtica ( 43 1) 3 1 = 0 e
comparemos os resultados que se obtm usando inteiros ou reais:
_$ (- (* (- (/ 4 3) 1) 3) 1)
-1
_$ (- (* (- (/ 4.0 3.0) 1.0) 3.0) 1.0)
-2.22045e-016

Como se pode ver, nem usando inteiros, nem usando reais, se consegue
obter o resultado correcto. No caso dos inteiros, o problema causado pela
diviso inteira de 4 por 3 que produz 1. No caso dos reais, o problema
causado por erros de arrendondamento: 4/3 no representvel com um
nmero finito de dgitos. Este erro de arrendondamento ento propagado
nas restantes operaes produzindo um valor que, embora no seja zero,
est muito prximo.
Obviamente, como o resultado da avaliao com reais suficientemente
pequeno, podemos convert-lo para o tipo inteiro (aplicando-lhe uma truncatura com a funo fix) e obtemos o resultado correcto:
_$ (fix (- (* (- (/ 4.0 3.0) 1.0) 3.0) 1.0))
0

No entanto, esta soluo tambm tem problemas. Consideremos a


diviso 0.6/0.2 = 3:
_$ (/ 0.6 0.2)
3.0
_$ (fix (/ 0.6 0.2))
2

O problema ocorre porque os computadores usam o sistema binrio de


representao e, neste sistema, no possvel representar os nmeros 0.6 e
0.2 com um nmero finito de dgitos binrios e, consequentemente, ocorrem erros de arredondamento. Por este motivo, o resultado da diviso, em
34

vez de ser 3, 2.999999999999999555910790149937, embora o Auto Lisp o


apresente como 3.0. No entanto, ao eliminarmos a parte fraccionria com a
funo fix, o que fica apenas o inteiro 2.
Igualmente perturbante o facto de, embora 0.4 7 = 0.7 4 = 2.8,
em Auto Lisp, (* 0.4 7) 6= (* 0.7 4): ambas as expresses tm um valor que escrito pelo Auto Lisp como 2.8 mas h diferentes erros de arrendondamento cometidos nas duas expresses que tornam os resultados
diferentes.
Note-se que, mesmo quando no h erros de arrendondamento nas operaes, a reduzida preciso com que o Auto Lisp escreve os resultados pode
induzir-nos em erro. Por exemplo, embora internamente o Auto Lisp saiba
que (/ 1.000001 10) produz 0.1000001, ele apenas escreve 0.1 como
resultado.
Exerccio 3.13.1 Pretende-se criar um lano de escada com n espelhos capaz de vencer uma
determinada altura a em metros. Admitindo que cada degrau tem uma altura do espelho h
e uma largura do cobertor d que verificam a proporo
2h + d = 0.64
defina uma funo que, a partir da altura a vencer e do nmero de espelhos, calcula o
comprimento do lano de escada.

3.14

Smbolos e Avaliao

Vimos que os smbolos avaliam para aquilo a que estiverem associados no


momento da avaliao. Por este motivo, os smbolos so usados para dar
nomes s coisas. O que mais interessante que os smbolos so, eles prprios, coisas!
Para percebermos esta caracterstica dos smbolo vamos apresentar uma
funo pr-definida do Auto Lisp que nos permite saber o tipo de uma entidade qualquer: type. Consideremos a seguinte interaco:
_$ (type
INT
_$ (type
STR
_$ (type
REAL
_$ (type
USUBR
_$ (type
SUBR

1)
"Bom dia!")
pi)
quadrado)
+)

35

Como podemos ver, a funo type devolve-nos o tipo do seu argumento: INT para inteiros, STR para strings, REAL para reais, etc.
Mas o que so estes resultadosINT, STR, REAL, etc que foram devolvidos pela funo type? Que tipo de objectos so? Para responder
questo, o melhor usar a mesmssima funo type:
_$ (type pi)
REAL
_$ (type (type pi))
SYM

SYM a abreviatura de symbol, indicando que o objecto que devolvido pela funo type um smbolo. Note-se que, contrariamente ao que
acontece com o smbolo pi que est associado ao nmero 3.141519 . . . , os
smbolos REAL, INT, STR, etc., no esto associados a nada, eles apenas so
usados como representao do nome de um determinado tipo de dados.
Se os valores devolvidos pela funo type so objectos do tipo smbolo,
ento dever ser possvel design-los, tal como designamos os nmeros ou
as strings. Mas qual ser o modo de o fazermos? Uma hiptese (errada) seria escrev-lo tal como escrevemos nmeros ou strings. Acontece que isto
possvel no caso dos nmeros e strings pois eles avaliam para eles prprios.
J no caso dos smbolos, sabemos que no avaliam para eles prprios, antes avaliando para as entidades a que esto associados naquele momento.
Assim, se quisermos designar o smbolo pi, no bastar escrev-lo numa
expresso pois o que ir resultar aps a sua avaliao no ser um smbolo
mas sim o nmero 3.141519 . . . que o valor desse smbolo.
Para ultrapassar este problema precisamos de, por momentos, alterar a
semntica habitual que o Lisp atribui aos smbolos. Essa semntica, recordemonos, a de que o valor de um smbolo a entidade a que esse smbolo est
associado nesse momento e esse valor surge sempre que o Lisp avalia o
smbolo. Para alterarmos essa semntica, precisamos de indicar ao Lisp
que no queremos que ele avalie um determinado smbolo, i.e., queremos
que ele trate o smbolo como ele , sem o avaliar. Para isso, o Lisp disponibiliza a forma quote. Esta forma, que recebe um nico argumento,
tem uma semntica simplicssima: devolve o argumento sem este ter sido
avaliado. Reparemos na seguinte interaco:
_$ pi
3.14159
_$ (quote pi)
PI
_$ (+ 1 2 3)

36

6
_$ (quote (+ 1 2 3))
(+ 1 2 3)

Como se v, qualquer que seja o argumento da expresso (quote ),


o valor da expresso o prprio sem ter sido avaliado.
A razo de ser do quote est associada distino que existe entre as
frases Diz-me o teu nome e Diz-me o teu nome . No primeiro caso
a frase tem de ser completamente interpretada para que o ouvinte possa
dizer qual o seu prprio nome. No segundo caso, as plicas esto l para
indicar ao ouvinte que ele no deve interpretar o que est entre plicas e
deve limitar-se a dizer a frase o teu nome. As plicas servem, pois, para
distinguir o que deve ser tomado como e o que deve ser interpretado.
Para simplificar a escrita de formas que empregam o quote, o Lisp
disponibiliza uma abreviatura que tem exactamente o mesmo significado:
a plica (). Quando o Lisp est a fazer a leitura de uma expresso e encontra
algo da forma , aquilo que lido , na realidade, (quote ). Assim,
temos:
_$ pi
PI
_$ (+ 1 2 3)
(+ 1 2 3)
Exerccio 3.14.1 Qual o significado da expresso pi?

Combinao de Dados

Vimos, nas seces anteriores, alguns dos tipos de dados pr-definidos em


Auto Lisp. Em muitos casos, esses tipos de dados so os necessrios e
suficientes para nos permitir criar os nossos programas mas, noutros casos,
ser necessrio introduzirmos novos tipos de dados. Nesta seco iremos
estudar o modo de o fazermos e iremos exemplific-lo com um tipo de
dados que nos ser particularmente til: coordenadas.

4.1

Coordenadas

A Arquitectura pressupe a localizao de elementos no espao. Essa localizao expressa-se em termos do que se designa por coordenadas: as coordenadas de um ponto identificam univocamente esse ponto no espao.
Se o espao for bi-dimensional (abreviadamente, 2D), cada ponto identificado por duas coordenadas. Se o espao for tri-dimensional (3D), cada
37

x
Figura 2: Coordenadas rectangulares e polares.
ponto identificado por trs coordenadas. Por agora, vamos considerar
apenas o caso de coordenadas bi-dimensionais.
Existem vrios sistemas de coordenadas bi-dimensionais possveis mas
os mais usais so o sistema de coordenadas rectangulares e o sistemas de
coordenadas polares, representados na Figura 2. No sistema de coordenadas rectangulares, esse par de nmeros designa a abcissa x e a ordenada y.
No sistema de coordenadas polares, esse par de nmeros designa o raio vector e o ngulo polar . Em qualquer caso, as coordenadas bi-dimensionais
so descritas por um par de nmeros.
Como vimos nas seces anteriores, o Auto Lisp sabe lidar com o conceito de nmeros. Vamos agora ver que ele tambm sabe lidar com o conceito de par.

4.2

Pares

Em Lisp, podemos criar pares atravs da funo pr-definida cons. Esta


funo, cujo nome a abreviatura da palavra construct, aceita quaisquer
duas entidades como argumentos e produz um par com essas duas entidades. Tradicionalmente, usual dizer que o par um cons.10
Eis um exemplo da criao de um par de nmeros:
_$ (cons 1 2)
(1 . 2)

Como podemos ver pela interaco anterior, quando o Lisp pretende


escrever o resultado da criao de um par, ele comea por escrever um
10

O cons para o Lisp o mesmo que as tabelas (arrays) e estruturas (records, structs)
so para as outras linguagens como Pascal ou C. Na prtica, o cons um mecanismo de
agregao de entidades.

38

abrir parnteses, depois escreve o primeiro elemento do par, depois escreve


um ponto de separarao, depois escreve o segundo elemento do par e,
finalmente, escreve um fechar parnteses. Esta notao denomina-se par
com ponto ou, no original, dotted pair.
Quando o Lisp cria um par de elementos, cria uma associao interna
entre esses elementos que podemos representar graficamente como uma
caixa dividida em duas metades, cada uma apontando para um dos elementos. Por exemplo, o par anterior pode ser representado graficamente
da seguinte forma:

A representao grfica anterior denomina-se notao de caixa e ponteiro precisamente porque mostra os pares como caixas com ponteiros
para os elementos dos pares.
Uma vez que a construo de um par feita usando uma funo, a sua
invocao segue as mesmas regras de avaliao de todas as outras invocaes de funes, ou seja, as expresses que constituem os argumentos
so avaliadas e so os resultados dessas avaliaes que so usados como
elementos do par. Assim, temos:
_$ (cons (+ 1 2) (* 3 4))
(3 . 12)
_$ (cons 1 "dois")
(1 . "dois")
_$ (cons (area-circulo 10) (area-triangulo 20 30))
(314.159 . 300)

A propsito deste ltimo exemplo, note-se a diferena entre o ponto


relativo parte fraccionria do primeiro nmero e o ponto relativo separao dos elementos do par. Na primeira ocorrncia, o ponto faz parte da
sintaxe do nmero fraccionrio e no pode ter espaos. Na segunda ocorrncia, o ponto faz parte da sintaxe dos pares e tem de ter espaos.
A partir do momento em que temos um par de entidades, podemos estar interessados em saber quais so os elementos do par. Em Lisp, dado um
par de entidades (um cons) podemos obter o primeiro elemento do par
usando a funo car e podemos obter o segundo elemento do par usando
a funo cdr.

39

_$ (car (cons 1 2))


1
_$ (cdr (cons 1 2))
2

Note-se que aplicar o car ou o cdr a um cons no afecta esse cons,


apenas diz qual o primeiro ou segundo elemento do cons. Em termos
da representao grfica de caixa e ponteiro, a aplicao das funes car
e cdr corresponde, simplesmente, a seguir os apontadores da esquerda e
direita, respectivamente.
Os nomes car e cdr tm raizes histricas e, embora possa no parecer,
so relativamente fceis de decorar. Uma mnemnica que pode ajudar
pensar que o cAr obtm o elemento que vem Antes e o cDr obtm o
elemento que vem Depois.
Uma vez que a funo cons forma pares com os seus argumentos,
igualmente possvel formar pares de pares. Os seguintes exerccios exemplificam esta situao.
Exerccio 4.2.1 Qual o resultado da avaliao da expresso (cons (cons 1 2) 3)?
Exerccio 4.2.2 Qual o resultado da avaliao da expresso (car (cons (cons 1 2) 3))?
Exerccio 4.2.3 Qual o resultado da avaliao da expresso (cdr (cons (cons 1 2) 3))?

4.3

Operaes com Coordenadas

A partir do momento em que sabemos construir pares, podemos criar coordenadas e podemos definir operaes sobre essas coordenadas. Para criarmos coordenadas podemos simplesmente juntar num par os dois nmeros
que representam a abcissa e a ordenada. Por exemplo, o ponto do espao
cartesiano (1, 2) pode ser construdo atravs de:
_$ (cons 1 2)
(1 . 2)

Uma vez que estamos a fazer um par que contm, primeiro, a abcissa e,
depois, a ordenada, podemos obter estes valores usando, respectivamente,
as funes car e cdr.
Para melhor percebermos a utilizao destas funes, imaginemos que
pretendemos definir uma operao que mede a distncia d entre os pontos
P0 = (x0 , y0 ) e P1 = (x1 , y1 ) que podemos ver na Figura 3. A distncia d
corresponde, logicamente, ao comprimento da recta que une P0 a P1 .
40

P1

y1
d
y0

P0
x0

x1

Figura 3: Distncia entre dois pontos.


A aplicao do teorema de Pitgoras permite-nos determinar a distncia d em termos das coordenadas dos pontos P0 a P1 :
p
d = (x1 x0 )2 + (y1 y0 )2
A traduo desta frmula para Auto Lisp consiste da seguinte definio:
(defun distancia-2d (p0 p1)
(sqrt (+ (quadrado (- (car p1) (car p0)))
(quadrado (- (cdr p1) (cdr p0))))))

Podemos agora experimentar a funo com um caso concreto:


_$ (distancia-2d (cons 2 3) (cons 8 6))
6.7082

4.4

Abstraco de Dados

Embora tenhamos pensado na operao distancia-2d como uma funo


que recebe as coordenadas de dois pontos, quer quando observamos a definio da funo, quer quando observamos a sua utilizao subsequente,
o que vemos a invocao das operaes cons, car e cdr e, consequentemente, nada nos diz que a funo esteja a lidar com coordenadas. Embora o
conceito de coordenadas esteja presente nos nossos pensamentos, os nossos
programas apenas mostram que estamos a construir e a manipular pares.

41

Esta diferena entre os conceitos que temos na nossa cabea e os que


empregamos na programao torna-se ainda mais evidente quando pensamos noutras entidades matemticas que, tal como as coordenadas, tambm agrupam elementos mais simples. Um nmero racional, por exemplo,
define-se como um par de dois nmeros inteiros, o numerador e o denominador. semelhana do que fizemos com as coordenadas, tambm poderiamos criar um nmero racional em Auto Lisp custa da funo cons e
poderiamos seleccionar o numerador e o denominador custa das funes
car e cdr. Outro exemplo ser a implementao de nmeros complexos.
Estes tambm so constitudos por pares de nmeros reais e tambm poderiam ser implementados custa das mesma funes cons, car e cdr.
medida que formos implementando em Auto Lisp as funes que
manipulam estes conceitos, a utilizao sistemtica das funes cons, car
e cdr far com que seja cada vez mais difcil perceber qual ou quais os
tipo de dados a que se destina uma determinada funo. De facto, a partir
apenas das funes cons, car e cdr no podemos saber se estamos a lidar
com coordenadas, racionais, complexos ou qualquer outro tipo de dados
que tenhamos implementado em termos de pares.
Para resolvermos este problema necessrio preservarmos no Auto
Lisp o conceito original que pretendemos implementar. Para isso, temos
de abstrair a utilizao que fazemos dos pares, escondendo-a no interior
de funes que representem explicitamente os conceitos originais. Vamos
agora exemplificar esta abordagem reconsiderando o conceito de coordenadas cartesianas bi-dimensionais e introduzindo funes apropriadas para
esconder a utilizao dos pares.
Assim, para construirmos as coordenadas (x, y) a partir dos seus componentes x e y, ao invs de usarmos directamente a funo cons vamos
definir uma nova funo que vamos denominar de xy:11
(defun xy (x y)
(cons x y))

A construo de coordenadas bi-dimensionais por intermdio da funo xy apenas o primeiro passo para abstrairmos a utilizao dos pares.
O segundo passo a criao de funes que acedem abcissa x e ordenada y das coordenadas (x, y). Para isso, vamos definir, respectivamente,
11

Naturalmente, podemos considerar outro nome igualmente apropriado como, por


exemplo, coordenadas-cartesianas-2d. Contudo, como de esperar que tenhamos
frequentemente de criar coordenadas, conveniente que adoptemos um nome suficientemente curto e, por isso, vamos adoptar o nome xy.

42

as funes cx e cy como abreviaturas de coordenada x e coordenada y:12


(defun cx (c)
(car c))
(defun cy (c)
(cdr c))

As funes xy, cx e cy constituem uma abstraco das coordenadas bidimensionais que nos permitem escrever as funes que manipulam coordenadas sem termos de pensar na sua implementao em termos de pares.
Esse facto torna-se evidente quando reescrevemos a funo distancia-2d
em termos destas novas funes:
(defun distancia-2d (p0 p1)
(sqrt (+ (quadrado (- (cx p1) (cx p0)))
(quadrado (- (cy p1) (cy p0))))))

Reparemos que, contrariamente ao que acontecia com a primeira verso


desta funo, a leitura da funo d agora uma ideia clara do que ela faz.
Ao invs de termos de pensar em termos de car e cdr, vemos que a funo
lida com as coordenadas x e y. A utilizao da funo tambm mostra
claramente que o que ela est a manipular so coordenadas:
_$ (distancia-2d (xy 2 3) (xy 8 6))
6.7082

A introduo das operaes de coordenadas xy, cx e cy no s torna


mais claro o significado dos nossos programas como facilita bastante a
definio de novas funes. Agora, ao invs de termos de nos lembrar
que as coordenadas so pares cujo primeiro elemento a abcissa e cujo
segundo elemento a ordenada, basta-nos pensar nas operaes bsicas
de coordenadas e definir as funes que pretendermos em termos delas.
Por exemplo, imaginemos que pretendemos definir uma operao que faz
deslocar um ponto de uma determinada distncia, expressa em termos
de um comprimento x e de uma altura y , tal como est apresentado na
Figura 4. Como ser evidente pela Figura, sendo P = (x, y), ento teremos P 0 = (x + x , y + y ). Para simplificar o uso desta funo, vamos
12

Tal como a funo xy poderia ter um nome mais explcito, tambm as funes
que acedem abcissa e ordenada se poderiam denominar abcissa e ordenada ou
coordenada-x e coordenada-y ou qualquer outro par de nomes suficientemente claro.
No entanto, sendo as coordenadas um tipo de dados com muito uso, mais uma vez se tornar vantajoso que empreguemos nomes curtos.

43

P0
y
P

Figura 4: Deslocamento de um ponto.


denomin-la de +xy. Naturalmente, ela precisa de receber, como parmetros, o ponto de partida P e os incrementos x e y que iremos denominar
de dx e dy, respectivamente.
A definio da funo fica ento:
(defun +xy (p dx dy)
(xy (+ (cx p) dx)
(+ (cy p) dy)))

Uma vez que esta funo recebe coordenadas como argumento e produz coordenadas como resultado, ela constitui outra importante adio ao
conjunto de operaes disponveis para lidar com coordenadas. Naturalmente, podemos usar a funo +xy para definir novas funes como, por
exemplo, os casos particulares de deslocamento horizontal e vertical que se
seguem:
(defun +x (p dx)
(+xy p dx 0))
(defun +y (p dy)
(+xy p 0 dy))

4.5

Coordenadas Tri-Dimensionais

As coordenadas bi-dimensionais localizam pontos num plano. A localizao de pontos no espao requer coordenadas tri-dimensionais. Tal como
acontecia com as coordenadas bi-dimensionais, tambm existem vrios sistemas de coordenadas tri-dimensionais, nomeadamente, o sistema de coordenadas rectangulares, o sistema de coordenadas cilndricas e o sistema
44

x
P

x
Figura 5: Coordenadas Cartesianas
de coordenadas esfricas. Em qualquer deles, a localizao de um ponto no
espao feita custa de trs parmetros independentes. As coordenadas
tri-dimensionais so, por isso, triplos de nmeros.
Nesta seco vamos debruar-nos apenas sobre o sistema de coordenadas rectangulares, tambm conhecidas por coordenadas Cartesianas em
honra ao seu inventor: Ren Descartes. Estas coordenadas esto representadas na Figura 5.
Como vimos na seco anterior, o Auto Lisp disponibiliza a operao
cons para permitir formar pares de elementos, o que nos permitiu traduzir as coordenadas bi-dimensionais da geometria para pares de nmeros
em Auto Lisp. Agora, a situao ligeiramente mais complexa pois o Auto
Lisp no disponibiliza nenhuma operao capaz de fazer triplos de nmeros. Ou qudruplos. Ou quntuplos. Ou, na verdade, qualquer outro tipo
de tuplo para alm dos duplos.
Na realidade, esta aparente limitao do Auto Lisp perfeitamente justificada pois trivial, a partir de pares, formarmos qualquer tipo de agrupamento que pretendamos. Um triplo pode ser feito custa de dois pares: um
contendo dois elementos e outro contendo este par e o terceiro elemento.
Um qudruplo pode ser feito custa de trs pares: um par para cada dois
elementos e um terceiro par contendo os dois pares anteriores. Um quntuplo pode ser feito custa de quatro pares: trs pares para formar um
qudruplo e um quarto par para juntar o qudruplo ao quinto elemento.
Agrupamentos com maior nmero de elementos podem ser formados simplesmente seguindo esta estratgia.
45

Uma vez que um triplo de nmeros se pode implementar custa de


dois pares, vamos usar os mesmos dois pares para implementar coordenadas tri-dimensionais. Para abstrair a utilizao destes pares, vamos definir
a funo xyz para construir coordenadas tri-dimensionais e vamos definir
as funes cx, cy e cz para aceder s coordenadas x, y e z, respectivamente. Comecemos pela funo xyz:
(defun xyz (x y z)
(cons (cons x y) z))

Reparemos que a funo produz o triplo fazendo um par com os dois


primeiros elementos x e y e, em seguida, fazendo um par com o par anterior e com o terceiro elemento z. Em notao de caixa e ponteiro este
agrupamento de elementos tem a seguinte forma:

z
y

Como evidente, qualquer outro arranjo de pares que juntasse os trs


elementos seria igualmente vlido.
A partir do momento em que temos a funo xyz que constri as coordenadas, estamos em condies de definir as funes que acedem s suas
componentes:
(defun cx (c)
(car (car c)))
(defun cy (c)
(cdr (car c)))
(defun cz (c)
(cdr c))

Notemos que as funes que seleccionam os componentes das coordenadas tm de ser consistentes com os pares construdos pela funo xyz.
Uma vez que a coordenada x o elemento da esquerda do par que o elemento da esquerda do par que contm os componentes das coordenadas,
precisamos de empregar duas vezes a funo car na definio da funo
cx. Do mesmo modo, a coordenada y o elemento da direita do par que
46

o elemento da esquerda, pelo que temos primeiro de aplicar a funo car


para aceder ao par que contm x e y e temos de lhe aplicar a funo cdr
para aceder a y.
As duas ltimas combinaes podem ser feita de forma abreviada usando
as funes pr-definidas caar e cdar:
_$ (caar (cons (cons 1 2) 3))
1
_$ (cdar (cons (cons 1 2) 3))
2

Repare-se que a sequncia de letras a e d entre as letras c e r indica a


composio de funes em questo. Assim, a funo cadr significa o car
do cdr do argumento, i.e., (cadr c)=(car (cdr c)); e a funo caar
significa o car do car do argumento, i.e., (caar c)=(car (car c)).
Por tradio, o Auto Lisp define todas as possveis combinaes de at quatro invocaes das funes car e cdr. Assim, para alm das funes caar,
cadr, cdar e cddr que correspondem a todas as possveis invocaes de
duas funes, existem ainda as variaes de trs invocaes caaar, caadr,
cadar, etc., e as variaes de quatro invocaes caaaar, caaadr, caadar,
caaddr, etc. No entanto, deve-se evitar a utilizao destas combinaes de
funes como cadar, cdadar, cdar e, por vezes, at mesmo as car e cdr,
pois tornam o cdigo pouco claro. Para termos um pouco mais de clareza
devemos definir funes cujo nome seja suficientemente explicativo do que
a funo faz.
A partir do momento em que temos coordenadas tri-dimensionais podemos definir operaes que lidem com elas. Uma funo til a que mede
a distncia entre dois pontos no espao tri-dimensional. Ela corresponde a
uma generalizao trivial da funo distancia-2d onde necessrio ter
em conta a coordenada z dos dois pontos. Num espao tri-dimensional, a
distncia d entre os pontos P0 = (x0 , y0 , z0 ) e P1 = (x1 , y1 , z1 ) dada por
p
d = (x1 x0 )2 + (y1 y0 )2 + (z1 z0 )2
Traduzindo est definio para Auto Lisp, temos:
(defun distancia-3d (p0
(sqrt (+ (quadrado ((quadrado ((quadrado (-

p1)
(cx p1) (cx p0)))
(cy p1) (cy p0)))
(cz p1) (cz p0))))))

semelhana do que fizemos para as coordenadas bi-dimensionais,


tambm podemos definir operadores para incrementar as coordenadas tri47

dimensionais de um ponto de uma determinada distncia especificada


em termos das projeces x , y e z :
(defun +xyz (p dx dy dz)
(xyz (+ (cx p) dx)
(+ (cy p) dy)
(+ (cz p) dz)))

Tal como acontecia com a funo +xy, tambm a funo +xyz independente do arranjo particular de pares que usemos para implementar as
coordenadas tri-dimensionais. Desde que mantenhamos a consistncia entre as funes cx, cy e cz, que seleccionam os componentes das coordenadas tri-dimensionais, e a funo xyz que cria coordenadas tri-dimensionais
a partir dos seus componentes, podemos livremente empregar outros arranjos de pares.
Exerccio 4.5.1 Defina a funo +z que, dado um ponto em coordenadas tri-dimensionais,
calcula as coordenadas do ponto que lhe est directamente por cima distncia z .
Exerccio 4.5.2 Defina a funo ponto-medio que calcula as coordenadas tridimensionais
do ponto mdio entre dois outros pontos P0 e P1 descritos tambm pelas suas coordenadas
tridimensionais.

4.6

Coordenadas Bi- e Tri-Dimensionais

Embora tenhamos conseguido implementar quer coordenadas bi-dimensionais,


quer coordenadas tri-dimensionais, as suas implementaes em termos de
pares so intrinsecamente incompatveis. Este problema claramente visvel, por exemplo, na implementao da operao que selecciona a coordenada x que, na verso para coordenadas bi-dimensionais, tem a forma
(defun cx (c)
(car c))

e, na verso para coordenadas tri-dimensionais, tem a forma


(defun cx (c)
(car (car c)))

Obviamente, no possvel termos duas funes diferentes com o mesmo


nome, pelo que temos de escolher qual das verses pretendemos usar e, por
arrastamento, qual o nmero de dimenses que pretendemos usar.
Ora, do ponto de vista matemtico, as coordenadas bi-dimensionais so
apenas um caso particular das coordenadas tri-dimensionais e, portanto,
48

seria desejvel podermos ter uma nica operao cx capaz de obter a coordenada x quer de coordenadas bi-dimensionais, quer de coordenadas tridimensionais. Logicamente, o mesmo podemos dizer da operao cy.
Para isso, necessrio repensarmos a forma como implementamos as
coordenadas em termos de pares, de modo a encontrarmos um arranjo de
pares que seja utilizvel por ambos os tipos de coordenadas. Para simplificar, podemos comear por considerar que, semelhana do que fizemos
para o caso bi-dimensional, usamos um primeiro par para conter a coordenada x, tal como se apresenta em seguida:

Falta agora incluirmos a coordenada y no caso bi-dimensional e as coordenadas y e z no caso tri-dimensional. Se o objectivo termos um arranjo
de pares que funcione para os dois casos, ento crucial que a coordenada
y fique armazenada de forma idntica em ambos. Uma vez que as coordenadas tri-dimensionais exigem pelo menos mais um par, podemos arbitrar
a seguinte soluo:

x
y

Embora no caso bi-dimensional no seja preciso armazenar mais nada,


um par sempre composto por dois elementos, pelo que temos de decidir o
que guardar no lugar assinalado com ?. Uma vez que para coordenadas
bi-dimensionais no h nada para guardar nesse lugar, podemos empregar
um elemento qualquer que signifique o nada, o vazio. A linguagem Lisp
disponibiliza a constante nil precisamente para esse fim. O nome nil
uma contraco da palavra nihil que, em latim, significa o vazio.
Empregando a constante nil, as coordenadas bi-dimensionais (x, y)
passam a ter a seguinte implementao:

49

x
y

nil

Para construir a estrutura anterior, a funo xy passa a ter a seguinte


definio:
(defun xy (x y)
(cons x (cons y nil)))

No caso das coordenadas tri-dimensionais, poderamos substituir o ?


pelo valor da coordenada z mas, tal como considermos as coordenadas bidimensionais como um caso particular das coordenadas tri-dimensionais,
tambm as coordenadas tri-dimensionais podem ser vistas como um caso
particular das coordenadas tetra-dimensionais e assim sucessivamente.13
Isto sugere que devemos repetir a mesma estrutura medida que vamos aumentando o nmero de dimenses. Assim, para as coordenadas
tri-dimensionais (x, y, z), podemos conceber a seguinte organizao:

x
y
z

nil

Obviamente, precisamos de redefinir a funo xyz:


(defun xyz (x y z)
(cons x (cons y (cons z nil))))

fcil constatarmos que as duas estruturas anteriores so perfeitamente


compatveis no sentido de podermos ter uma s verso das operaes cx
e cy que funcione quer para coordenadas bi-dimensionais, quer para coordenadas tri-dimensionais. De acordo com a notao de caixa e ponteiro
apresentada, podemos definir:
13

Embora, do ponto de vista da Arquitectura clssica, no seja necessrio trabalhar com


mais do que trs coordenadas, as modernas ferramentas de visualizao podem necessitar
de trabalhar com coordenadas tetra-dimensionais, por exemplo, para lidarem com o tempo.

50

(defun cx (c)
(car c))
(defun cy (c)
(car (cdr c)))
(defun cz (c)
(car (cdr (cdr c))))

importante referirmos que esta redefinio das funes no perturba


as funes j definidas +xy, distancia-2d e distancia-3d. De facto,
desde que se mantenha a consistncia entre as funes que constriem as
coordenadas e as que seleccionam os seus componentes, nada ser afectado.

4.7

A Notao de Lista

O arranjo de pares que adoptmos para implementar coordenadas no


original, sendo usado desde a inveno da linguagem Lisp para implementar agrupamentos com um nmero arbitrrio de elementos. A esses
agrupamentos d-se o nome de listas. As listas so um tipo de dados muito
utilizado na linguagem Lisp e o nome Lisp , na verdade, um acrnimo de
List Processing.
De forma a tornar o uso de listas mais simples, o Lisp no s disponibiliza um conjunto de operaes especificamente destinadas a lidar com listas
como imprime as listas de forma especial: quando o segundo elemento de
um par outro par ou nil, o Lisp escreve o resultado sob a forma de uma
lista de elementos:
_$ (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)

Repare-se, no exemplo anterior, que os pares foram escritos usando


uma notao diferente que evita o ponto necessrio para representar os
dotted pairs e evita tambm escrever a constante nil. Esta notao intencionalmente usada pelo Lisp para facilitar a leitura de listas pois, quando
se pensa numa lista como uma sequncia de elementos prefervel ver esses elementos dispostos em sequncia a v-los como um aglomerado de
pares. Contudo, convm no esquecermos que, internamente, a lista de
facto implementada usando um aglomerado de pares.
Dada uma lista de elementos, qual o significado de lhe aplicar as operaes car e cdr? Para responder a esta pergunta podemos simplesmente
experimentar:
51

_$ (car (cons 1 (cons 2 (cons 3 nil))))


1
_$ (cdr (cons 1 (cons 2 (cons 3 nil))))
(2 3)

Note-se que a funo car obteve o primeiro elemento da lista enquanto


que a funo cdr obteve os restantes elementos da lista, i.e., a lista a partir (inclusive) do segundo elemento. Isto sugere que podemos ver uma
lista como sendo composta por um primeiro elemento seguido do resto da
lista. Segundo este raciocnio, depois de termos obtido o ltimo elemento
da lista, o resto da lista dever ser uma lista vazia, i.e., uma lista sem quaisquer elementos. Para testarmos esta ideia podemos aplicar sucessivamente
a funo cdr a uma dada lista:
_$ (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)
_$ (cdr (cons 1 (cons 2 (cons 3 nil))))
(2 3)
_$ (cdr (cdr (cons 1 (cons 2 (cons 3 nil)))))
(3)
_$ (cdr (cdr (cdr (cons 1 (cons 2 (cons 3 nil))))))
nil

Reparemos que, medida que fomos aplicando sucessivamente a funo cdr, a lista foi encolhendo, i.e., fomos acedendo a partes sucessivamente mais pequenas da lista. No limite, quando aplicmos o cdr lista
(3) que j s tinha um elemento, estaramos espera de obter a lista vazia
(), i.e, a lista sem quaisquer elementos mas, no seu lugar, obtivmos nil.
Tal deve-se ao facto de as listas do Lisp serem apenas um artifcio de visualizao sobre um arranjo particular de pares e, na realidade, a operao
cdr continua a fazer o que sempre fez: acede ao segundo elemento do par
que, no caso de uma lista com um s elemento apenas a constante nil.
Por este motivo, para alm de representar o vazio, usual considerar que a
constante nil tambm representa a lista vazia.
Tal como as funes car e cdr podem ser vistas como operaes sobre
listas, tambm a funo cons o pode ser. Reparemos na seguinte interaco:
_$ (cons 1 nil)
(1)
_$ (cons 1 (cons 2 nil))
(1 2)
_$ (cons 1 (cons 2 (cons 3 nil)))
(1 2 3)

52

Como podemos ver, quando o segundo argumento de um cons uma


lista (vazia ou no), o resultado visto como a lista que resulta de juntar o
primeiro argumento do cons no incio daquela lista.
Para simplificar a criao de listas, o Lisp disponibiliza uma funo que
recebe qualquer nmero de argumentos e que constroi uma sequncia de
pares com os sucessivos argumentos de forma a constituirem uma lista:
_$ (list 1 2 3 4)
(1 2 3 4)
_$ (list 1 2)
(1 2)
_$ (list 1)
(1)

Como evidente, uma expresso da forma (list e1 e2 ... en ) absolutamente idntica a (cons e1 (cons e2 (... (cons en nil) ...))).
Obviamente, quando recebe zero argumentos a funo list produz
uma lista vazia:
_$ (list)
nil
Exerccio 4.7.1 Qual o significado da expresso (list (list))?
Exerccio 4.7.2 Quantos elementos tem a lista resultante de (list (list (list)))?
Exerccio 4.7.3 Qual o significado da expresso (cons (list 1 2) (list 3 4))?

4.8

tomos

Vimos que os pares (e as listas) permitem-nos criar aglomeraes de elementos e que podemos posteriormente aceder aos componentes dessas aglomeraes usando as operaes car e cdr. De facto, atravs de combinaes de cars e cdrs conseguimos aceder a qualquer elemento que faa
parte da aglomerao.
O que no possvel, contudo, usar car ou cdr para aceder ao interior de um desses elementos. Por exemplo, no possvel, usando as operaes car ou cdr, aceder a um carcter qualquer dentro de uma string.
Por este motivo, as strings dizem-se atmicas, i.e., no decomponveis. Os
nmeros, obviamente, tambm so atmicos.
Sendo uma lista uma aglomerao de elementos, natural pensar que
uma lista no atmica. Contudo, h uma lista em particular que merece

53

um pouco mais de ateno: a lista vazia. Uma lista vazia no decomponvel pois, de facto no contm nenhum elemento que se possa obter usando
o car nem nenhuns restantes elementos que se possam obter usando o
cdr.14 Se no se pode usar nem o car nem o cdr, isso sugere que a lista
vazia , na realidade, um tomo e, de facto, o Auto Lisp assim o considera.
O facto de a lista vazia ser, simultaneamente, um tomo e uma lista provoca um aparente paradoxo na definio de atomicidade pois faz com que
exista uma listaa lista vaziaque aparenta ser simultaneamente atmica
e no atmica. No entanto, o parodoxo apenas aparente pois, na realidade, a definio de atomicidade apenas exclui pares. Uma vez que a lista
vazia no um par, ela atmica, embora seja uma lista.15
Exerccio 4.8.1 Classifique, quanto atomicidade, o resultado da avaliao das seguintes
expresses:
1. (cons 1 2)
2. (list 1 2)
3. (strcat "Bom" " " "Dia")
4. nil
5. (+ 1 2 3)
6. (list 1)
7. (list)
8. (car (cons 1 (cons 2 nil)))
9. (cdr (cons 1 (cons 2 nil)))

4.9

Tipos Abstractos

A utilizao das operaes xyz, cx, cy e cz permite-nos definir um novo


tipo de dadosas coordenadas cartesianas tri-dimensionaise, mais importante, permite-nos abstrair a implementao desse tipo de dados, i.e.,
esquecer o arranjo particular de pares que o implementa. Por este motivo,
denomina-se este novo tipo de dados por tipo abstracto. Ele abstracto porque apenas existe no nosso pensamento. Para o Lisp, como vimos, as coordenadas so apenas arranjos particulares de pares. Esse arranjo particular
denomina-se representao dos elementos do tipo e, como vimos quando
14

Na realidade, alguns dialectos de Lisp, incluindo o Auto Lisp, consideram que possvel aplicar as funes car e cdr lista vazia, obtendo-se, em ambos os casos, a lista vazia.
15
Nesta matria, a documentao do Auto Lisp est simplesmente incorrecta pois afirma
que tudo o que no for uma lista um tomo, esquecendo-se de excluir a lista vazia que,
obviamente, uma lista mas tambm um tomo.

54

mudmos de um particular arranjo de pares para outro, possvel alterarmos a representao de um tipo abstracto sem afectar os programas que
usam o tipo abstracto.
Um tipo abstracto caracterizado apenas pelas suas operaes e estas
podem ser divididas em dois conjuntos fundamentais: os construtores que,
a partir de argumentos de tipos apropriados, produzem elementos do tipo
abstracto, e os selectores que, a partir de um elemento do tipo abstracto, produzem os seus constituintes. Existem ainda outras categorias de operaes
mas, por agora iremos concentrarmo-nos apenas nestas duas.
No caso das coordenadas cartesianas tri-dimensionais, o conjunto dos
construtores apenas contm a funo xyz, enquanto que o conjunto dos selectores contm as funes cx, cy e cz. Para um tipo abstracto, a relao
entre os construtores e os selectores crucial pois eles tm de ser consistentes entre si. Matematicamente, essa consistncia assegurada por equaes
que, no caso presente, se podem escrever da seguinte forma:
(cx (xyz x y z)) = x
(cy (xyz x y z)) = y
(cz (xyz x y z)) = z
Se modificarmos a representao de coordenadas mas mantivermos a
consistncia entre os construtores e os selectores, manter-se- tambm o
correcto funcionamento do tipo abstracto. Foi por este motivo que nos foi
possvel refazer a implementao das coordenadas bi- e tri-dimensionais
sem afectar as funes j definidas +xy, distancia-2d e distancia-3d.
Exerccio 4.9.1 O que um construtor de um tipo abstracto?
Exerccio 4.9.2 O que um selector de um tipo abstracto?
Exerccio 4.9.3 O que a representao de um tipo abstracto?
Exerccio 4.9.4 Dado um ponto P0 = (x0 , y0 ) e uma recta definida por dois pontos P1 =
(x1 , y1 ) e P2 = (x2 , y2 ), a distncia mnima d do ponto P0 recta obtm-se pela frmula:
d=

|(x2 x1 )(y1 y0 ) (x1 x0 )(y2 y1 )|


p
(x2 x1 )2 + (y2 y1 )2

Defina uma funo denominada distancia-ponto-recta que, dadas as coordenadas


dos pontos P0 , P1 e P2 , devolve a distncia mnima de P0 recta definida por P1 e P2 .

55

4.10

Coordenadas em AutoCad

Como mostrmos nas seces anteriores, a compatibilizao das operaes


que lidam com coordenadas bi- e tri-dimensionais levou-nos a escolher
represent-las por intermdio de listas. Para alm de esta representao ser
mais simples de ler, tem ainda outra enorme vantagem: totalmente compatvel com a forma como as funcionalidades do AutoCad esperam receber
coordenadas. De facto, como iremos ver, o prprio AutoCad necessita que
as coordenadas das entidades geomtricas que pretendemos criar sejam especificadas como listas de nmeros: dois nmeros no caso das coordenadas
bi-dimensionais e trs nmeros no caso das coordenadas tri-dimensionais.
Seria ento de esperar que j existissem pr-definidas em Auto Lisp as
operaes que lidam com coordenadas mas, na realidade, tal no acontece
porque, na altura em que o Auto Lisp foi criado a teoria dos tipos abstractos
ainda no tinha sido efectivamente posta em prtica. Nessa altura, no
se tinha a noo clara das vantagens desta abordagem mas tinha-se uma
noo muito clara de um dos seus problemas: piorava a performance dos
programas.
De facto, bvio que, a partir de uma lista representando as coordenadas de um ponto, mais rpido obter a coordenada x usando a funo car
do que usando o selector cx que, indirectamente, invoca a funo car.
Como, na altura em que a teoria dos tipos abstractos apareceu, os computadores ainda no eram suficientemente rpidos para que os programadores se pudessem dar ao luxo de desperdiar performance, a penalizao
induzida pela teoria no foi bem aceite durante algum tempo.16 Quando
os computadores se tornaram suficientemente rpidos, a situao mudou e
os programadores passaram a considerar que as vantagens da utilizao de
tipos abstractos ultrapassava largamente a cada vez mais residual desvantagem da perda de performance. Actualmente, ponto assente que devemos
sempre usar tipos abstractos.
Infelizmente, o Auto Lisp foi desenvolvido h demasiados anos e no
se encontra to actualizado como seria desejvel. Se fizermos uma anlise
dos programas Auto Lisp existentes, incluindo programas desenvolvidos
recentemente, iremos constatar que a prtica usual a manipulao directa da representao dos tipos, saltando por cima de qualquer definio
de tipos abstractos. Em particular, a manipulao de coordenadas feita
16

Essa penalizao variava de linguagem para linguagem. Nalgumas linguagens, ditas


estaticamente tipificadas, a penalizao poderia ser pequena. Noutras, ditas dinamicamente
tipificadas, a penalizao era geralmente mais elevada. O Lisp insere-se no grupo das linguagens dinamicamente tipificadas.

56

usando directamente as operaes do tipo lista, i.e., construindo coordenadas com a funo list e acedendo aos seus valores com as funes car,
cadr e caddr. Acontece que esta violao do tipo abstracto ocorre, actualmente, muito mais por razes histricas do que por razes de performance.
Embora sejamos grandes defensores do respeito pragmtica do Auto
Lisp, neste caso particular vamos adoptar uma abordagem diferente: por
motivos de clareza dos programas, de facilidade da sua compreenso e de
facilidade da sua correco, vamos empregar as operaes do tipo abstracto
coordenadas, nomeadamente os construtores xy e xyz e os selectores cx,
cy e cz e vamos evitar, sempre que possvel, aceder directamente representao das coordenadas, i.e., vamos evitar usar as funes list, car,
cadr e caddr para manipular coordenadas. Isso no impede que usemos
essas funes para outros fins, em particular, para manipular listas. Uma
vez que as coordenadas esto implementadas usando listas, esta distino
poder parecer confusa mas, na verdade, no : as coordenadas no so
listas embora a representao das coordenadas seja uma lista.
Naturalmente, quando o leitor consultar programas escritos por outros
programadores de Auto Lisp dever ter em conta estas subtilezas e dever
conseguir perceber se uma dada lista presente num programa significa coordenadas ou se significa outro tipo abstracto qualquer.

4.11

Coordenadas Polares

Uma das vantagens da utilizao dos tipos abstractos de informao que


se torna fcil desenvolver novas operaes com base nas operaes do tipo.
Por exemplo, embora o tipo coordenadas bi-dimensionais esteja descrito
em termos de coordenadas rectangulares, nada nos impede de definir operaes para manipular coordenadas polares.
Tal como representado da Figura 6, uma posio no plano bi-dimensional
descrita pelos nmeros x e ysignificando, respectivamente, a abcissa
e a ordenadaenquanto que a mesma posio em coordenadas polares
descrita pelos nmeros e significando, respectivamente, o raio vector
(tambm chamado mdulo) e o ngulo polar (tambm chamado argumento).
Com a ajuda da trigonometria e do teorema de Pitgoras conseguimos facilmente relacionar estas coordenadas entre si:
(
x = cos
y = sin

57

x
Figura 6: Coordenadas rectangulares e polares.

p
= x2 + y 2
= arctan y
x
Com base nas equaes anteriores, podemos agora definir a funo pol
(abreviatura de polar) que contri coordenadas a partir da sua representao polar simplesmente convertendo-a para a representao rectangular
equivalente.
(defun pol (ro fi)
(xy (* ro (cos fi))
(* ro (sin fi))))

Assim sendo, o tipo abstracto coordenadas polares fica representado em


termos do tipo coordenadas rectangulares. Por este motivo, os selectores
do tipo coordenadas polaresa funo pol-ro que nos permite obter o
mdulo e a funo pol-fi que nos permite obter o argumento tero
de usar as operaes do tipo coordenadas rectangulares:
(defun pol-ro (c)
(sqrt (+ (quadrado (cx c)) (quadrado (cy c)))))
(defun pol-fi (c)
(atan (cy c) (cx c)))

Eis alguns exemplos do uso destas funes:17


_$ (pol (sqrt 2) (/ pi 4))
(1.0 1.0)
17
Note-se, nestes exemplos, que alguns valores das coordenadas no so zero como
seria expectvel, mas sim valores muito prximos de zero que resultam de erros de
arredondamento.

58

P0

Figura 7: O deslocamento de um ponto em coordenadas polares.


_$ (pol 1 0)
(1.0 0.0)
_$ (pol 1 (/ pi 2))
(6.12323e-017 1.0)
_$ (pol 1 pi)
(-1.0 1.22465e-016)

Uma outra operao bastante til a que, dado um ponto P = (x, y)


e um vector com origem em P e descrito em coordenadas polares por
uma distncia e um ngulo , devolve o ponto localizado na extremidade
destino do vector, tal como visualizado na Figura 7. A trigonometria
permite-nos facilmente concluir que as coordenadas do ponto destino so
dadas por P 0 = (x + cos , y + sin ).
A traduo directa da funo para Lisp :
(defun +pol (p ro fi)
(xy (+ (cx p) (* ro (cos fi)))
(+ (cy p) (* ro (sin fi)))))

Como exemplos de utilizao, temos:


_$ (+pol (xy 1 2) (sqrt 2) (/ pi 4))
(2.0 3.0)
_$ (+pol (xy 1 2) 1 0)
(2.0 2.0)
_$ (+pol (xy 1 2) 1 (/ pi 2))
(1.0 3.0)

Embora a funo +pol funcione perfeitamente, a sua definio no


to simples quanto poderia ser. Isso evidente quando a comparamos com
a da funo +xy (que repetimos de seguida):
59

(defun +xy (p dx dy)


(xy (+ (cx p) dx)
(+ (cy p) dy)))

relativamente evidente que a funo +pol est a fazer parte do trabalho que j feito pela funo +xy, nomeadamente no que diz respeito
soma das coordenadas do ponto p com as componentes que resultam da
converso de coordenadas polares para rectangulares. Assim, possvel
simplificar a funo +pol escrevendo apenas:
(defun +pol (p ro fi)
(+xy p
(* ro (cos fi))
(* ro (sin fi))))

Uma observaco atenta das funes +xy e +pol permite-nos constatar


que elas partilham um mesmo comportamento: ambas recebem um ponto
e um vector a somar a esse ponto para produzir um novo ponto: esse
vector descrito pelos parmetros x e y no primeiro caso e por e
no segundo.
O facto de ambas as funes partilharem um comportamento leva-nos
naturalmente a pensar na sua possvel generalizao, atravs da definio de uma nica funo +c (abreviatura de +coordenada) que, dado um
ponto P0 e outro ponto P1 representando a extremidade final de um vector
com extremidade inicial na origem, devolve o ponto que resulta de somar
esse vector ao primeiro ponto, tal como est ilustrado da Figura 8. Naturalmente, quer o ponto P0 quer o ponto P1 podem ser especificados em
qualquer sistema de coordenadas desde que seja possvel relacionar esse
sistema com o sistema de coordenadas rectangulares.
Para definirmos esta funo a abordagem mais simples ser explorar as
propriedades aditivas das coordenadas rectangulares:
(defun +c (p0 p1)
(xy (+ (cx p0) (cx p1))
(+ (cy p0) (cy p1))))

Uma vez que as coordenadas polares esto representadas em termos


de coordenadas rectangulares, mesmo quando especificamos coordenadas
polares a operao +c continua a funcionar correctamente.

4.12

A funo command

A partir do momento em que sabemos construir coordenadas passamos a


poder utilizar um conjunto muito vasto de operaes grficas do Auto Lisp.
60

P0

P0

P1
y

x
Figura 8: O deslocamento de um ponto por adio de um vector.
Existem trs maneiras diferentes de se invocar essas operaes grficas
mas, por agora, vamos explorar apenas a mais simples: a funo command.
A funo command aceita um nmero arbitrrio de expresses que vai
avaliando e passando os valores obtidos para o AutoCad medida que o
AutoCad os vai requerendo.18 No caso de utilizao mais comum, a funo command recebe uma cadeia de caracteres que descreve o comando
AutoCad que se pretende executar seguido de qualquer nmero de argumentos que sero usados como os dados necessrios para a execuo desse
comando.
A vantagem da funo command permitir criar programas completos
que criam entidades grficas tal como um normal utilizador de AutoCad o
poderia fazer de forma manual. A ttulo de exemplo, consideremos a criao de um crculo. Para se criar um crculo em AutoCad pode-se usar o
comando circle e fornecer-lhe as coordenadas do centro e o raio do crculo. Se pretenderemos criar o mesmo crculo a partir do Auto Lisp, ao
invs de invocarmos interactivamente o comando AutoCad circle e de
lhe fornecermos os dados necessrios, podemos simplesmente invocar a
funo command e passar-lhe todos os dados necessrios como argumentos, comeando pela string "circle", seguida das coordenadas do centro
do crculo e, por ltimo, seguida do raio do crculo.
Para concretizarmos o exemplo, consideremos a criao de um crculo
de raio r = 1 centrado na posio (x, y) = (2, 3). A invocao Auto Lisp
18

Este comportamento mostra que, na realidade, command no pertence ao conjunto das


funes normais pois, ao contrrio destas, s avalia os argumentos medida que eles vo
sendo necessrios.

61

que cria este crculo a seguinte:


_$ (command "circle" (list 2 3) 1)
nil

Como se pode ver pelo exemplo, as coordenadas do centro do crculo


foram especificadas atravs da criao de uma lista. No entanto, como referimos na seco 4.10, vamos evitar a especificao de coordenadas directamente em termos da sua representao como listas e vamos, em seu lugar,
usar as operaes do tipo. Assim, a expresso anterior fica mais correcta na
seguinte forma:
_$ (command "circle" (xy 2 3) 1)
nil

Como se pode ver tambm, a invocao da funo command devolve


nil como resultado.
Um outro exemplo da utilizao desta funo na colocao de um
texto na nossa rea de desenho. Para isso, podemos usar novamente a funo command mas, desta vez, os argumentos a passar sero outros, nomeadamente, a cadeia de caracteres "text" que designa o comando desejado, as coordenadas onde pretendemos colocar o canto inferior esquerdo
do texto, um nmero que representa a altura do texto, um nmero que representa o ngulo (em radianos) que a base do texto dever fazer com a
horizontal e, finalmente, uma cadeia de caracteres com o texto a inserir. Eis
um exemplo:
_$ (command "text" (xy 1 1) 1 0 "AutoCad")
nil

O resultado da avaliao das duas invocaes anteriores visvel da


Figura 9.
Finalmente, a funo command pode ser usada para alterar os parmetros de funcionamento do AutoCad. Por exemplo, para desligar os Object
Snaps podemos fazer:
_$ (command "osnap" "off")
nil

importante memorizar esta ltima expresso pois, sem a sua invocao, todos os usos da funo command estaro sob o efeito de Object Snaps,
fazendo com que as coordenadas que indicamos para as nossas figuras geomtricas no sejam estritamente respeitadas pelo AutoCad, que as arredondar de acordo com a malha do Object Snaps.
62

Figura 9: Um crculo com o texto "AutoCad".


Como exemplo da utilizao de comandos em Auto Lisp, a Figura 10
mostra o resultado da avaliao das seguintes expresses onde usamos coordenadas polares para desenhar crculos e texto:
(command "erase" "all" "")
(command "circle" (pol 0 0) 4)
(command "text" (+pol (pol 0 0) 5 0)
1 0 "Raio: 4")
(command "circle" (pol 4 (/ pi 4)) 2)
(command "text" (+pol (pol 4 (/ pi 4)) 2.5 0)
0.5 0 "Raio: 2")
(command "circle" (pol 6 (/ pi 4)) 1)
(command "text" (+pol (pol 6 (/ pi 4)) 1.25 0)
0.25 0 "Raio: 1")
(command "zoom" "e")
Exerccio 4.12.1 Refaa o desenho apresentado na Figura 10 mas utilizando apenas coordenadas rectangulares.
Exerccio 4.12.2 Defina uma funo denominada circulo-e-raio que, dadas as coordenadas do centro do crculo e o raio desse crculo, cria o crculo especificado no AutoCad e,
semelhana do que se v na Figura 10, coloca o texto a descrever o raio do crculo direita
do crculo. O texto dever ter um tamanho proporcional ao raio do crculo.
Exerccio 4.12.3 Utilize a funo circulo-e-raio definida na pergunta anterior para reconstituir a imagem apresentada na Figura 10.

Existem algumas particularidades do comportamento da funo command


que importante discutir. Para as compreendermos preciso recordar que
a funo command simula a interaco do utilizador com o AutoCad.
Consideremos ento um exemplo de uma interaco: imaginemos que estamos em frente interface do AutoCad e pretendemos apagar todo o contedo da nossa rea de desenho no AutoCad. Para isso, podemos invocar
63

Figura 10: Crculos e textos especificados usando coordenadas polares.


o comando erase do AutoCad, o que podemos fazer escrevendo as letras e-r-a-s-e e premindo enter no final. Acontece que o comando no
pode ser imediatamente executado pois o AutoCad precisa de saber mais
informao, em particular, o que que para apagar. Para isso, o AutoCad pede-nos para seleccionar um ou mais objectos para apagar. Nessa
altura, se respondermos com a palavra all e premirmos novamente a tecla enter, o AutoCad selecciona todos os objectos mas fica a aguardar que
indiquemos que terminmos a seleco de objectos, o que se pode fazer
premindo simplesmente a tecla enter.
Ora para que a funo command possa simular a interaco que acabmos de descrever, ela tem de passar ao AutoCad todas as informaes
necessrias e tem de o fazer medida que o AutoCad precisa delas. Assim, cada sequncia de teclas que foi por ns dada ao AutoCad dever ser
agora dada na forma de string como argumento funo command. No entanto, como no faz muito sentido obrigar o utilizador a especificar, numa
string o premir da tecla enter, a funo command assume que, aps passar
todos os caracteres de uma string ao AutoCad, deve passar-lhe tambm o
correspondente premir da tecla enter.
Tentemos agora, luz desta explicao, construir a invocao da funo
command que simula a interaco anterior. O primeiro passo da avaliao
da expresso , como referimos, o passar da sequncia de letras e-r-a-s-e
seguidas do premir virtual da tecla enter, o que podemos fazer escrevendo:

64

(command "erase" ...)

Em seguida, sabemos que o AutoCad nos vai pedir para seleccionarmos


os objectos a apagar, pelo que escrevemos a string com a palavra all que
a funo ir passar ao AutoCad, novamente terminando-a com o premir
virtual da tecla enter:
(command "erase" "all" ...)

Finalmente, sabemos que depois desta interaco, o AutoCad vai continuar espera que indiquemos que terminmos a seleco de objectos atravs do premir da tecla enter, o que implica que temos de indicar funo
command para simplesmente passar o tal premir virtual da tecla enter.
Ora j sabemos que a funo faz isso automaticamente aps passar os caracteres de uma string pelo que, se no queremos passar caracteres alguns
mas apenas a tecla enter ento, logicamente, temos de usar uma string
vazia, ou seja:
(command "erase" "all" "")

Assim, a string vazia, quando usada como argumento de command,


equivalente a premir a tecla enter na linha de comandos do AutoCad.
claro que, se na sequncia de uma invocao da funo command, o
AutoCad ficar espera de dados, novas invocaes da funo command podero providenciar esses dados ou o utilizador poder dar essa informao
directamente no AutoCad. Isto quer dizer que a invocao anterior pode
tambm ser feita na forma:
(command "erase")
(command "all")
(command "")

Como se pode constatar pelo exemplo anterior, a funo command termina assim que conseguir passar todos os argumentos que tem para o AutoCad, independentemente de eles serem suficientes ou no para o AutoCad conseguir completar o pretendido. Se no forem, o AutoCad limita-se
a continuar espera que lhe forneamos, via command ou directamente na
interface, os dados que lhe faltam, o que nos permite implementar um processo de colaborao com o AutoCad: parte do que se pretende feito no
lado do Auto Lisp e a parte restante no lado do AutoCad.
Por exemplo, imaginemos que pretendemos criar um crculo num dado
ponto mas queremos deixar ao utilizador a escolha do raio. Uma soluo
65

ser iniciar a construo do crculo centrado num dado ponto atravs da


funo command mas aguardar que o utilizador termine essa construo
indicando explicitamente no AutoCad (por exemplo, usando o rato) qual
o raio do crculo pretendido. Assim, iniciariamos o comando com:
(command "circle" (xy 0 0))

e o utilizador iria ento ao AutoCad terminar a criao do crculo atravs


da indicao do raio.
Um problema um pouco mais complicado ser criar um crculo de raio
fixo mas deixar ao utilizador a escolha do centro. A complicao deriva de
no conhecermos as coordenadas do centro mas querermos passar o raio:
se nos falta o argumento do meio, ou seja, a posio do centro do crculo,
no podemos passar o do fim, ou seja, o raio do crculo.
H vrias solues para este problema mas, por agora iremos explorar
apenas uma. Para contornar o problema, o AutoCad permite a utilizao
do carcter especial \ para indicar que se pretende suspender momentaneamente a passagem de argumentos para o AutoCad, para se retomar essa
passagem assim que o AutoCad obtenha o valor correspondente ao argumento que no foi passado. Para facilitar a legibilidade dos programas, o
smbolo pause designa precisamente a string que contm esse nico carcter. A seguinte interaco mostra um exemplo da utilizao deste smbolo
para resolver o problema da criao de um crculo de raio igual a 3 mas
centro especificado pelo utilizador. Note-se que, tal como referido na Tabela 1, o carcter \ um carcter de escape e por isso que tem de ser
escrito em duplicado.
_$ pause
"\\"
_$ (command "circle" pause 3)
nil

Um outro aspecto importante da funo command prende-se com a passagem de nmeros e coordenadas (representadas por listas de nmeros).
Embora nos exemplos anteriores estes tipos de dados tenham sido directamente passadas funo command, na realidade tambm podem ser passados simulando o premir das teclas correspondentes. Isto quer dizer que
possvel criar um crculo com centro no ponto (2, 3) e raio 1 atravs da
expresso:
(command "circle" "2,3" "1")

66

O que acontece, na prtica, que os argumentos da funo command,


para alm de s serem avaliados quando so necessrios, so tambm automaticamente convertidos para o formato pretendido pelo AutoCad. Isto
permite-nos trabalhar as propriedades das entidades geomtricas no formato que nos mais conveniente (nmeros, coordenadas, etc) e deixar
funo command a responsabilidade de converter os valores para o formato
apropriado para o AutoCad.
Exerccio 4.12.4 Pretendemos colocar duas circunferncias de raio unitrio em torno da
origem de modo a que fiquem encostadas uma outra, tal como se ilustra no seguinte
desenho:

Escreva uma sequncia de expresses que, quando avaliadas, produzem a figura anterior.
Exerccio 4.12.5 Pretendemos colocar quatro circunferncias de raio unitrio em torno da
origem de modo a que fiquem encostadas umas s outras, tal como se ilustra no seguinte
desenho:

Escreva uma sequncia de expresses que, quando avaliadas, produzem a figura anterior.
Exerccio 4.12.6 Pretendemos colocar trs circunferncias de raio unitrio em torno da origem de modo a que fiquem encostadas umas s outras, tal como se ilustra no seguinte
desenho:

Escreva uma sequncia de expresses que, quando avaliadas, produzem a figura anterior.

67

4.13

Variantes de Comandos

Quando fornecemos um nome na linha de comando do AutoCad, ele tenta


perceber o que pedido seguindo um algoritmo que involve os seguintes
passos:19
1. O nome pesquisado na lista de comandos do AutoCad. Se o nome
est na lista, ento, se o nome precedido de um ponto ., executa
o comando e termina, caso contrrio pesquisa o nome na lista de comandos no-definidos20 e, se no o encontrar, executa o comando e
termina.21
2. O nome pesquisado na lista de comandos externos definidos no ficheiro de parmetros acad.pgp. Se o encontrar, executa o comando
e termina.
3. O nome pesquisado na lista de comandos definidos pelo Auto Lisp.
Se o encontrar ento, primeiro, verifica se um comando de carregamento automtico e, se for, o programa correspondente carregado
e, segundo, executa o comando e termina.
4. O nome pesquisado na lista de sinnimos (aliases) definidos no ficheiro de parmetros do programa. Se o encontrar, substitui o nome
pela sua expanso e volta ao princpio.
5. Termina com uma mensagem de erro indicando que o nome desconhecido.
Repare-se que o algoritmo anterior fala de comandos cujo nome precedido de um ponto, de comandos no definidos, comandos externos, etc.
Todas estas variedades existem porque, na realidade, possvel redefinir
ou tornar como no definidos os comandos pr-definidos do AutoCad. Isto
implica que quando invocamos um comando como, por exemplo, circle,
estamos na realidade a invocar a mais recente definio que foi feita para
esse comando. S no caso de no ter sido feita qualquer redefinio do comando e de este no ter sido tornado no definido que iremos invocar
19

Por motivos pedaggicos, esta descrio uma simplificao da realidade.


Um comando no-definido no a mesma coisa que um comando indefinido. O primeiro
corresponde a um comando conhecido que foi explicitamente declarado como no definido
enquanto o segundo corresponde a um comando totalmente desconhecido.
21
Este comportamento permite que se possam tornar comandos como no-definidos e,
ainda assim, pode execut-los desde que se preceda o seu nome de um ponto.
20

68

a definio original. A consequncia que, no sabendo se foi feita uma


redefinio ou se o comando no foi tornado como no definido, no podemos ter a certeza do que vai realmente ser executado.
Como se pode ver pelo algoritmo descrito acima, o AutoCad admite
uma variante para todos os comandos: se o nome do comando comear
com o carcter ponto . ento feita a execuo do comando pr-definido
do AutoCad. Desta forma, este pequeno truque permite-nos ultrapassar
qualquer redefinio ou no definio de um comando.
Uma outra caracterstica do AutoCad que pode complicar a utilizao
da funo command a internacionalizao: em diferentes pases, o AutoCad utiliza os nomes dos comandos traduzidos para diferentes lnguas.
Por exemplo, o comando circle escreve-se kreis no AutoCad Alemo,
cercle no Francs, cerchio no Italiano, krunice no Checo e circulo
no Espanhol. Assim, se tivermos feito um programa que invoca o comando
circle e tentarmos executar esse comando num AutoCad preparado para
outra lngua, iremos obter um erro por o comando ser desconhecido nessa
lngua.
Para evitar este problema, o AutoCad admite ainda outra variante para
todos os comandos: se o nome do comando comear com o carcter _
ento feita a execuo do comando na lngua original do AutoCad, que
o Ingls.
A combinao destas duas variantes possvel. O nome _.circle (ou
._circle) indica a verso original do comando, independentemente de
alteraes lingusticas ou redefinies do comando.
Para evitar problemas, de agora em diante iremos sempre utilizar esta
conveno de preceder com os caracteres _. todos os comandos que
usarmos na operao command.
Exerccio 4.13.1 Redefina e teste a funo circulo-e-raio de modo a usar os comandos
pr-definidos do AutoCad, independentemente de redefinies de comandos ou mudanas
de lngua.
Exerccio 4.13.2 Defina a funo comando-pre-definido que, dado o nome de um comando em ingls, devolve esse nome convenientemente modificado para permitir aceder
ao comando correspondente pr-definido no AutoCad independentemente da lngua em
que este esteja.

4.14

ngulos em Comandos

Alguns dos comandos do AutoCad necessitam de saber raios e ngulos


em simultneo. Por exemplo, para criar um polgono regular, o comando

69

polygon necessita de saber quantos lados ele dever ter, qual o centro do
polgono, se ele vai estar inscrito numa circunferncia ou circunscrito numa
circunferncia e, finalmente o raio dessa circunferncia e o ngulo que o
primeiro vrtice do polgono faz com o eixo dos x. Este dois ltimos
parmetros, contudo, no podem ser fornecidos separadamente, sendo necessrio especific-los de uma forma conjunta, atravs de uma string em
que se junta o raio e o ngulo separados pelo carcter < (representando
um ngulo) e precedidos do carcter @ (representando um incremento
relativamente ao ltimo ponto dado, que era o centro da circunferncia).
Por exemplo, o raio 2 com um ngulo de 30o escreve-se "@2<30". Note-se
que o ngulo tem de estar em graus. Se, ao invs de fornecermos esta string
fornecermos apenas um nmero, o AutoCad ir tratar esse nmero como o
raio e, mesmo que lhe tentemos fornecer outro nmero para o ngulo, ir
assumir que o ngulo zero.
Uma vez que, na maior parte dos casos, os raios e ngulos sero parmetros das nossas funes e ainda que, do ponto de vista matemtico,
prefervel trabalhar em radianos, conveniente definir uma funo que,
a partir do raio e ngulo em radianos, produz a string apropriada com o
ngulo em graus. Para isso, conveniente usarmos a funo strcat para
concatenarmos as strings parciais que sero produzidas atravs da funo
rtos que converte um nmero numa string. Assim, temos:
(defun raio&angulo (raio angulo)
(strcat "@"
(rtos raio)
"<"
(rtos (graus<-radianos angulo))))

Usando esta funo agora trivial criarmos polgonos com diferentes


ngulos de rotao. Por exemplo, a seguinte sequncia de comandos produz a imagem representada na Figura 11:
(command "_.polygon" 3
(xy 0 0) "_Inscribed"
(command "_.polygon" 3
(xy 0 0) "_Inscribed"
(command "_.polygon" 4
(xy 3 0) "_Inscribed"
(command "_.polygon" 4
(xy 3 0) "_Inscribed"
(command "_.polygon" 5
(xy 6 0) "_Inscribed"
(command "_.polygon" 5
(xy 6 0) "_Inscribed"

(raio&angulo 1 0))
(raio&angulo 1 (/ pi 3)))
(raio&angulo 1 0))
(raio&angulo 1 (/ pi 4)))
(raio&angulo 1 0))
(raio&angulo 1 (/ pi 5)))

70

Figura 11: Tringulos, quadrados e pentgonos sobrepostos com diferentes


ngulos de rotao.

4.15

Efeitos Secundrios

Vimos anteriormente que qualquer expresso Lisp tem um valor. No entanto, aquilo que se pretende de uma expresso que use a funo command
no saber qual o seu valor mas sim qual o efeito que produzido num
determinado desenho. De facto, a execuo de um comando AutoCad produz, em geral, uma alterao do desenho actual, sendo irrelevante o seu
valor.
Este comportamento da funo command fundamentalmente diferente
do comportamento das funes que vimos at agora pois, anteriormente,
as funes eram usadas para computar algo, i.e., para produzir um valor
a partir da sua invocao com determinados argumentos e, agora, no o
valor que resulta da invocao do comando que interessa mas sim o efeito
secundrio (tambm chamado efeito colateral) que interessa.
Contudo, mesmo no caso em que apenas nos interessa o efeito secundrio, necessrio continuar a respeitar a regra de que, em Lisp, qualquer
expresso tem um valor e, por isso, tambm uma invocao de funo
tem de produzir um valor como resultado. por este motivo que a invocao da funo command devolve sempre nil como resultado. Obviamente que qualquer outro valor serviria (pois no suposto ser usado)
mas convenciona-se que nos casos em que no h um valor mais relevante
a devolver deve-se devolver nil (que, em Latim, significa nada).22
22

Em tutoriais de Auto Lisp aparece por vezes a sugesto de terminar a definio de


funes que apenas realizam efeitos secundrios com a expresso de escrita (princ). De
facto, quando invocada desta forma, a funo princ no s no escreve nada como aparenta
no devolver nada:
_$ (princ)
_$

No entanto, na realidade, a expresso anterior devolveu, de facto, um valor: um smbolo


cuja representao externa no escrita no terceiro passo do ciclo read-eval-print. Embora

71

Um dos aspectos importantes da utilizao de efeitos secundrios est


na possibilidade da sua composio. A composio de efeitos secundrios
faz-se atravs da sua sequenciao, i.e., da realizao sequencial dos vrios
efeitos. Na seco seguinte iremos ver um exemplo da composio de efeitos secundrios.
Exerccio 4.15.1 Considere a seguinte interaco com o AutoCad para a criao de um
tronco de cone onde apresentamos a negrito os dados fornecidos pelo utilizador:
Command: cone
Specify center point of base or [3P/2P/Ttr/Elliptical]: 1,1,1
Specify base radius or [Diameter] <5.0000>: 3
Specify height or [2Point/Axis endpoint/Top radius] <10.0000>: Top
Specify top radius <0.0000>: 2
Specify height or [2Point/Axis endpoint] <10.0000>: 4

Tendo em conta a interaco anterior, defina uma funo Auto Lisp denominada tronco-cone
cujos parmetros so o ponto P do centro da base do slido, a altura h do slido e os raios
rb e rt da base e do topo do slido e que cria o tronco de cone correspondente em AutoCad.

4.16

A Ordem Drica

Na Figura 12 apresentamos uma imagem do templo grego de Segesta. Este


templo, que nunca chegou a ser acabado, foi construdo no sculo quinto
antes de Cristo e representa um excelente exemplo da Ordem Drica, a mais
antiga das trs ordens da arquitectura Grega clssica. Nesta ordem, uma
coluna caracteriza-se por ter um fuste, um coxim e um baco. O baco
tem a forma de uma placa quadrada que assenta sobre o coxim, o coxim
assemelha-se a um tronco de cone invertido e assenta sobre o fuste, e o fuste
assemelha-se a um tronco de cone com vinte caneluras em seu redor. Estas
caneluras assemelham-se a uns canais semi-circulares escavados ao longo
da coluna.23 Quando os Romanos copiaram a Ordem Drica introduziramlhe um conjunto de alteraes, em particular, nas caneluras que, muitas
vezes, so simplesmente eliminadas.
invisvel, este smbolo especial no deixa de ser um valor e pode ser usado como qualquer
outro valor, embora a sua invisibilidade possa provocar resultados possivelmente menos
expectveis, como a seguinte comparao mostra:
_$ (cons 1 2)
(1 . 2)
_$ (cons (princ) (princ))
( . )

23
Estas colunas apresentam ainda uma deformao intencional denominada entasis. A
entasis consiste em dar uma ligeira curvatura coluna e, segundo alguns autores, destinase a corrigir uma iluso de ptica que faz as colunas direitas parecerem encurvadas.

72

Figura 12: O Templo Grego de Segesta, exemplificando alguns aspectos da


Ordem Drica. Este templo nunca chegou a ser terminado, sendo visvel,
por exemplo, a falta das caneluras nas colunas. Fotografia de Enzo De Martino.

73

(1, 11)

(1, 11)

(1, 10.5)

(1, 10.5)

(0.8, 10)

(0.8, 10)

x
(1, 0) (0, 0) (1, 0)

Figura 13: Uma coluna Drica de referncia.


Nesta seco vamos esquematizar o desenho de uma coluna Drica
(sem caneluras). Do mesmo modo que uma coluna Drica se pode decompor nos seus componentes fundamentaiso fuste, o coxim e o baco
tambm o desenho da coluna se poder decompor no desenho dos seus
componentes. Assim, vamos definir funes para desenhar o fuste, o coxim
e o baco. A Figura 13 apresenta um modelo de referncia. Comecemos por
definir uma funo para o desenho do fuste:
(defun fuste ()
(command "_.line"
(xy -0.8 10)
(xy -1 0)
(xy 1 0)
(xy 0.8 10)
(xy -0.8 10)
""))

Neste exemplo, a funo command executa o comando AutoCad line


que, dada uma sequncia de pontos, constri uma linha poligonal com vrtices nesses pontos. Para se indicar o fim da sequncia de pontos usa-se
uma string vazia. Naturalmente, a invocao da funo fuste ter, como
efeito secundrio, a criao do fuste da coluna na rea de desenho do AutoCad. Uma outra possibilidade, eventualmente mais correcta, seria pedir
ao AutoCad a criao de uma linha fechada, algo que podemos fazer com a
74

opo "close" no lugar do ltimo ponto, i.e.:


(defun fuste ()
(command "_.line"
(xy -0.8 10)
(xy -1 0)
(xy 1 0)
(xy 0.8 10)
"_close"))

Note-se que o resultado do comando line a criao de um conjunto


de segmentos de recta. Cada um destes segmentos de recta uma entidade
individual que pode ser seleccionada e modificada independentemente dos
restantes. Para o caso de no pretendermos esse tratamento independente,
o AutoCad disponibiliza polilinhas (tambm conhecidas por plines). Estas
so criadas pelo comando pline que, neste contexto, difere do comando
line no facto de ter como resultado a criao de uma nica entidade composta pelos vrios segmentos.24
Para completar a figura, ainda necessrio definir uma funo para o
coxim e outra para o baco. No caso do coxim, o raciocnio semelhante:
(defun coxim ()
(command "_.line"
(xy -0.8 10)
(xy -1 10.5)
(xy 1 10.5)
(xy 0.8 10)
"_close"))

No caso do baco, podemos empregar uma abordagem idntica ou podemos explorar outro comando do AutoCad ainda mais simples destinado
construo de rectngulos. Este comando apenas necessita de dois pontos
para definir completamente o rectngulo:
(defun abaco ()
(command "_.rectangle"
(xy -1 10.5)
(xy 1 11)))

Finalmente, vamos definir uma funo que desenha as trs partes da


coluna:
24
Outras diferenas incluem o facto de as plines poderem ter espessura e estarem limitadas a um plano.

75

Figura 14: Uma coluna drica desenhada pelo AutoCad.


(defun coluna ()
(fuste)
(coxim)
(abaco))

Repare-se, na funo coluna, que ela invoca sequencialmente as funes fuste, coxim e, finalmente, abaco. Para percebermos o funcionamento da funo coluna importante saber que quando invocamos uma
funo que possui uma sequncia de expresses, o Lisp avalia sequencialmente cada uma das expresses, descartando o seu valor, at chegar
ltima cujo valor usado como valor final da funo. O facto de se descartarem todos os valores excepto o ltimo mostra que, numa sequenciao
de expresses, o que importa o efeito secundrio da sua avaliao. A
sequenciao o exemplo mais simples de uma estrutura de controle. Em
termos muito grosseiros, uma estrutura de controle um mecanismo das
linguagens de programao que indica ao computador a ordem pela qual
pretendemos que ele execute o programa.
A Figura 14 mostra o resultado da invocao da funo coluna.

4.17

Parameterizao de Figuras Geomtricas

Infelizmente, a coluna que crimos na seco anterior tem todas as suas


dimenses fixas, pelo que ser difcil encontrarmos outras situaes em que
possamos reutilizar a funo que definimos. Naturalmente, esta funo
seria mais til se a criao da coluna fosse parameterizvel, i.e., se a criao
76

rt

P1 = (x rt , y + a)

P2 = (x + rt , y + a)

P = (x, y)
P4 = (x rb , y)

rb

P3 = (x + rb , y)

Figura 15: Esquema do desenho do fuste de uma coluna.


dependesse dos vrios parmetros que caracterizam a coluna como, por
exemplo, as coordenadas da base da coluna, a altura do fuste, do coxim e
do baco, os raios da base e do topo do fuste, etc.
Para se compreender a parameterizao destas funes vamos comear
por considerar o fuste representado esquematicamente na Figura 15.
O primeiro passo para parameterizarmos um desenho geomtrico consiste na identificao dos parmetros relevantes. No caso do fuste, um dos
parmetros bvios a localizao espacial desse fuste, i.e., as coordenadas
de um ponto de referncia em relao ao qual fazemos o desenho do fuste.
Assim, comecemos por imaginar que o fuste ir ser colocado com o centro
da base num imaginrio ponto P de coordenadas arbitrrias (x, y). Para
alm deste parmetro, temos ainda de conhecer a altura do fuste a e os
raios da base rb e do topo rt do fuste.
Para mais facilmente idealizarmos um processo de desenho, conveniente assinalarmos no esquema alguns pontos de referncia adicionais. No
caso do fuste, uma vez que o seu desenho , essencialmente, um trapzio,
basta-nos idealizar o desenho deste trapzio atravs de uma sucesso de
linhas rectas dispostas ao longo de uma sequncia de pontos P1 , P2 , P3 e

77

rt

P2 = (x rt , y + a)

P = (x, y)
P1 = (x rb , y)

rb

P3 = (x + rt , y + a)
a
P4 = (x + rb , y)

Figura 16: Esquema do desenho do coxim de uma coluna.


P4 , pontos esses que conseguimos calcular facilmente a partir do ponto P .
Desta forma, estamos em condies de definir a funo que desenha
o fuste. Para tornar mais claro o programa, vamos empregar os nomes
a-fuste, r-base e r-topo para caracterizar a altura a, o raio da base rb
e o raio do topo rt , respectivamente. A definio fica ento:
(defun fuste (p a-fuste r-base r-topo)
(command "_.line"
(+xy p (- r-topo) a-fuste)
(+xy p (- r-base) 0)
(+xy p (+ r-base) 0)
(+xy p (+ r-topo) a-fuste)
"_close"))

Em seguida, temos de especificar o desenho do coxim. Mais uma vez,


convm pensarmos num esquema geomtrico, tal como apresentamos na
Figura 16.
Tal como no caso do fuste, a partir de um ponto P correspondente ao
centro da base do coxim, podemos computar as coordenadas dos pontos
que esto nas extremidades dos segmentos de recta que delimitam o desenho do coxim. Usando estes pontos, a definio da funo fica com a
seguinte forma:
(defun coxim (p a-coxim r-base r-topo)
(command "_.line"
(+xy p (- r-base) 0)
(+xy p (- r-topo) a-coxim)
(+xy p (+ r-topo) a-coxim)
(+xy p (+ r-base) 0)
"_close"))

Terminado o fuste e o coxim, preciso definir o desenho do baco. Para


isso, fazemos um novo esquema que apresentamos na Figura 17.

78

P2 = (x + 2l , y + a)
P = (x, y)
P1 = (x 2l , y)

Figura 17: Esquema do desenho do baco de uma coluna.


Mais uma vez, vamos considerar como ponto de partida o ponto P no
centro da base do baco. A partir deste ponto, podemos facilmente calcular os pontos P1 e P2 que constituem os dois extremos do rectngulo que
representa o alado do baco. Assim, temos:
(defun abaco (p a-abaco l-abaco)
(command "_.rectangle"
(+xy p (/ l-abaco -2.0) 0)
(+xy p (/ l-abaco +2.0) a-abaco)))

Finalmente, para desenhar a coluna completa, temos de combinar os


desenhos do fuste, do coxim e do baco. Apenas precisamos de ter em
conta que, tal como a Figura 18 demonstra, o raio do topo do fuste coincide
coincide com o raio da base do coxim e o raio do topo do coxim metade
da largura do baco. A mesma Figura mostra tambm que as coordenadas
da base do coxim correspondem a somar a altura do fuste s coordenadas
da base do fuste e as coordenadas da base do baco correspondem a somar
a altura do fuste e a altura do coxim s coordenadas da base do fuste.
Tal como fizemos anteriormente, vamos dar nomes mais claros aos parmetros da Figura 18. Usando os nomes p, a-fuste, r-base-fuste,
a-coxim, r-base-coxim, a-abaco e l-abaco no lugar de, respectivamente, P , af , rbf , ac , rbc , aa e la , temos:
(defun coluna (p
a-fuste r-base-fuste
a-coxim r-base-coxim
a-abaco l-abaco)
(fuste p a-fuste r-base-fuste r-base-coxim)
(coxim (+xy p 0 a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
(abaco (+xy p 0 (+ a-fuste a-coxim)) a-abaco l-abaco))

Com base nestas funes, podemos agora facilmente experimentar variaes de colunas. As seguintes invocaes produzem o desenho apresentado na Figura 19.
79

la
rbc

aa
ac

af

rbf

Figura 18: A composio do fuste, coxim e baco.

80

Figura 19: Variaes de colunas dricas.


(coluna
(coluna
(coluna
(coluna
(coluna
(coluna

(xy 0 0)
(xy 3 0)
(xy 6 0)
(xy 9 0)
(xy 12 0)
(xy 15 0)

9
7
9
8
5
6

0.5
0.5
0.7
0.4
0.5
0.8

0.4
0.4
0.5
0.3
0.4
0.3

0.3
0.6
0.3
0.2
0.3
0.2

0.3
0.6
0.2
0.3
0.1
0.4

1.0)
1.6)
1.2)
1.0)
1.0)
1.4)

Como bvio pela anlise desta figura, nem todas as colunas desenhadas obedecem aos cnones da ordem Drica. Mais frente iremos ver que
modificaes sero necessrias para evitar este problema.

4.18

Documentao

Na funo coluna, a-fuste a altura do fuste, r-base-fuste o raio


da base do fuste, r-topo-fuste o raio do topo do fuste, a-coxim a
altura do coxim, a-abaco a altura do baco e, finalmente, r-abaco
o raio do baco. Uma vez que a funo j tem vrios parmetros e o seu
significado poder no ser bvio para quem l a definio da funo pela
primeira vez, conveniente documentar a funo. Para isso, a linguagem
Lisp providencia uma sintaxe especial: sempre que surge o carcter ;, a
processo de leitura do Lisp ignora tudo o que vem a seguir at ao fim da
linha. Isto permite-nos escrever texto nos nossos programas sem correr o
risco de o Lisp tentar perceber o que l est escrito.
Usando documentao, o nosso programa completo para desenhar co-

81

lunas dricas fica com o seguinte aspecto:25


;;;;Desenho de colunas doricas
;;;O desenho de uma coluna dorica divide-se no desenho do
;;;fuste, do coxim e do abaco. A cada uma destas partes
;;;corresponde uma funcao independente.
;Desenha o fuste de uma coluna dorica.
;p: coordenadas do centro da base da coluna,
;a-fuste: altura do fuste,
;r-base: raio da base do fuste,
;r-topo: raio do topo do fuste.
(defun fuste (p a-fuste r-base r-topo)
(command "_.line"
;a criacao de linhas
(+xy p (- r-topo) a-fuste)
;com a funcao command
(+xy p (- r-base) 0)
;tem de ser terminada
(+xy p (+ r-base) 0)
;com a opcao "close"
(+xy p (+ r-topo) a-fuste)
;para fechar a figura
"close"))
;Desenha o coxim de uma coluna dorica.
;p: coordenadas do centro da base do coxim,
;a-coxim: altura do coxim,
;r-base: raio da base do coxim,
;r-topo: raio do topo do coxim.
(defun coxim (p a-coxim r-base r-topo)
(command "_.line"
(+xy p (- r-base) 0)
(+xy p (- r-topo) a-coxim)
(+xy p (+ r-topo) a-coxim)
(+xy p (+ r-base) 0)
"close"))
;para fechar a figura
;Desenha o abaco de uma coluna dorica.
;p: coordenadas do centro da base da coluna,
;a-abaco: altura do abaco,
;l-abaco: largura do abaco.
(defun abaco (p a-abaco l-abaco)
(command "_.rectangle"
(+xy p (/ l-abaco -2.0) 0)
(+xy p (/ l-abaco +2.0) a-abaco)))
;Desenha uma coluna dorica composta por fuste, coxim e abaco.
;p: coordenadas do centro da base da coluna,
;a-fuste: altura do fuste,
;r-base-fuste: raio da base do fuste,
;r-base-coxim: raio da base do coxim = raio do topo do fuste,
;a-coxim: altura do coxim,
;a-abaco: altura do abaco,
;l-abaco: largura do abaco = 2*raio do topo do coxim.
(defun coluna (p
25

O exemplo destina-se a mostrar as diferentes formas de documentao usadas em Lisp


e no a mostrar um exemplo tpico de programa documentado. Na verdade, o programa
to simples que no deveria necessitar de tanta documentao.

82

a-fuste r-base-fuste
a-coxim r-base-coxim
a-abaco l-abaco)
;;desenhamos o fuste com a base em p
(fuste p a-fuste r-base-fuste r-base-coxim)
;;colocamos o coxim por cima do fuste
(coxim (+y p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
;;e o abaco por cima do coxim
(abaco (+y p (+ a-fuste a-coxim)) a-abaco l-abaco))

Desta forma, quem for ler o nosso programa fica com uma ideia muito
mais clara do que cada funo faz, sem sequer precisar de ir estudar o corpo
das funes. Como se v pelo exemplo anterior, pragmtica usual em
Lisp usar um diferente nmero de caracteres ; para indicar a relevncia
do comentrio:
;;;; Devem comear na margem esquerda e servem para dividir o programa em seces e dar um ttulo a cada seco.
;;; Devem comear na margem esquerda e servem para fazer comentrios gerais ao programa que aparece em seguida. No se devem usar
no interior das funes.
;; Devem estar alinhadas com a parte do programa a que se vo aplicar,
que aparece imediatemente em baixo.
; Devem aparecer alinhados numa mesma coluna direita e comentam a
parte do programa imediatamente esquerda.
importante que nos habituemos a documentar as nossas definies
mas convm salientar que a documentao em excesso tambm tem desvantagens:
O cdigo Lisp deve ser suficientemente claro para que um ser humano o consiga perceber. sempre prefervel perder mais tempo a
tornar o cdigo claro do que a escrever documentao que o explique.
Documentao que no est de acordo com o programa pior que
no ter documentao.
frequente termos de modificar os nossos programas para os adaptar
a novos fins. Quanto mais documentao existir, mais documentao
necessrio alterar para a pr de acordo com as alteraes que tivermos feito ao programa.
83

Por estes motivos, devemos esforar-nos por escrever o cdigo mais


claro que nos for possvel e, ao mesmo tempo, providenciar documentao sucinta e til: a documentao no deve dizer aquilo que bvio a
partir da leitura do programa.
Exerccio 4.18.1 Considere o desenho de uma seta com origem no ponto P , comprimento
, inclinao , ngulo de abertura e comprimento da farpa , tal como se representa
em seguida:

Defina uma funo denominada seta que, a partir dos parmetros P , , , e ,


constri a seta correspondente.
Exerccio 4.18.2 Com base na soluo do exerccio anterior, defina uma funo que, dados
o ponto P , a distncia e o ngulo , desenha o norte tal como se apresenta no esquema
em baixo:

N
O desenho deve ainda obedecer s seguintes propores:
O ngulo de abertura da seta de 45 .
O comprimento da farpa de 2 .
O centro da letra N dever ser posicionado a uma distncia de
da seta segundo a direco da seta.

10

da extremidade

O tamanho da letra N dever ser metade da distncia .


Exerccio 4.18.3 Usando a funo seta, defina uma nova funo denominada seta-de-para
que, dados dois pontos, cria uma seta que vai do primeiro para o segundo ponto. As farpas
da seta devero ter comprimento unitrio e ngulo 8 .
Exerccio 4.18.4 Considere o desenho de uma habitao composta apenas por divises rectangulares. Pretende-se que defina a funo divisao-rectangular que recebe como
parmetros a posio do canto inferior esquerdo da diviso, o comprimento e a largura da
diviso e um texto a descrever a funo dessa diviso na habitao. Com esses valores a
funo dever construir o rectngulo correspondente e deve colocar no interior desse rectngulo duas linhas de texto, a primeira com a funo da diviso e a segunda com a rea da
diviso. Por exemplo, a sequncia de invocaes

84

(divisao-rectangular
(divisao-rectangular
(divisao-rectangular
(divisao-rectangular
(divisao-rectangular
(divisao-rectangular

(xy
(xy
(xy
(xy
(xy
(xy

0
4
6
0
5
8

0)
0)
0)
5)
5)
3)

4
2
5
5
3
3

3
3
3
4
4
6

"cozinha")
"despensa")
"quarto")
"sala")
"i.s.")
"quarto")

produz, como resultado, a habitao que se apresenta de seguida:

4.19

Depurao

Como sabemos, errare humanum est. O erro faz parte do nosso dia-a-dia e,
por isso, em geral, sabemos lidar com ele. J o mesmo no se passa com
as linguagens de programao. Qualquer erro num programa tem, como
consequncia, que o programa tem um comportamento diferente daquele
que era esperado.
Uma vez que fcil cometer erros, deve tambm ser fcil detect-los
e corrigi-los. actividade de deteco e correco de erros denomina-se
depurao. Diferentes linguagens de programao providenciam diferentes mecanismos para essa actividade. Neste domnio, como iremos ver, o
AutoCad est particularmente bem apetrechado.
Em termos gerais, os erros num programa podem classificar-se em erros
sintticos e erros semnticos.
4.19.1

Erros Sintticos

Os erros sintticos ocorrem quando escrevemos frases que no obedecem


gramtica da linguagem. Como exemplo prtico, imaginemos que pretendiamos definir uma funo que criava um nica coluna drica, que iremos
85

designar de coluna standard e que tem sempre as mesmas dimenses, no


necessitando de quaisquer outros parmetros. Uma possibilidade para a
definio desta funo ser:
(defun coluna-standard
(coluna (xy 0 0) 9 0.5 0.4 0.3 0.3 0.5))

No entanto, se avaliarmos aquela definio, o Auto Lisp ir apresentar


um erro, avisando-nos de que algo est errado:26
; error: bad DEFUN syntax:
(COLUNA-STANDARD (COLUNA (XY 0 0) 9 0.5 0.4 0.3 0.3 0.5))

O erro de que o Auto Lisp nos est a avisar de que a forma defun que
lhe demos para avaliar no obedece sintaxe exigida para essa forma e, de
facto, uma observao atenta da forma anterior mostra que no seguimos a
sintaxe exigida para uma definio e que, tal como discutimos na seco 3.6,
era a seguinte:
(defun nome (parmetro1 ... parmetron )
corpo)

O nosso erro agora bvio: esquecemo-nos da lista de parmetros. Se a


funo no tem parmetros a lista de parmetros vazia mas tem de estar
l na mesma. Uma vez que no estava, o Auto Lisp detecta e reporta um
erro sinttico, uma frase que no obedece sintaxe da linguagem.
H vrios outros tipos de erros sintticos que o Auto Lisp capaz de
detectar e que sero apresentados medida que os formos discutindo. O
importante, no entanto, no saber quais so os erros sintticos detectveis
pelo Auto Lisp, mas antes saber que o Auto Lisp capaz de verificar as expresses que escrevemos e fazer a deteco de erros sintticos antes mesmo
de as avaliar.
Para isso, o Visual Lisp disponibiliza na sua interface operaes que
fazem essa verificao para uma seleco ou para todo o ficheiro actual.
Na verso Inglesa do AutoCad, essas operaes denominam-se Check Selection ou Check Text in Editor e esto disponveis no menu Tools.
Como consequncia da invocao destas operaes, o AutoCad analiza a
seleco ou o ficheiro actual e escreve, numa janela parte, todos os erros
sintticos encontrados. Nesse janela, se clicarmos duas vezes sobre uma
mensagem de erro, somos conduzidos ao local do nosso programa onde o
erro ocorre.
26

Nem todos os dialectos e interpretadores de Lisp possuem exactamente este comportamento mas, variaes parte, os conceitos so os mesmos.

86

4.19.2

Erros Semnticos

Os erros semnticos so muito diferentes dos sintticos. Um erro semntico no um erro na escrita de uma frase da linguagem mas sim um
erro no significado dessa frase. Dito de outra forma, um erro semntico
ocorre quando escrevemos uma frase que julgamos ter um significado e, na
verdade, ela tem outro.
Em geral, os erros semnticos apenas so detectveis durante a invocao das funes que os contm. Parte dos erros semnticos detectvel
pelo avaliador de Lisp mas h inmeros erros cuja deteco s pode ser
feita pelo prprio programador.
Como exemplo de erro semntico consideremos uma operao sem significado como seja a soma de um nmero com uma string:
_$ (+ 1 "dois")
; error: bad argument type: numberp: "dois"

Como se pode ver, o erro explicado na mensagem que indica que o


segundo argumento devia ser um nmero. Neste exemplo, o erro suficientemente bvio para o conseguirmos detectar imediatamente. No entanto,
no caso de programas mais complexos, isso j poder no ser assim.
Na continuao do exemplo que apresentmos na discusso sobre erros
sintticos, consideremos a seguinte modificao funo coluna-standard
que corrige a falta de parmetros mas que introduz um outro erro:27
(defun coluna-standard ()
(coluna (xy O 0) 9 0.5 0.4 0.3 0.3 0.5))

Do ponto de vista sinttico, a funo est correcta. No entanto, quando


a invocamos surge um erro:
_$ (coluna-standard)
; error: bad argument type: numberp: nil

Como podemos ver pela resposta, o Auto Lisp protesta que um dos argumentos devia ser um nmero mas, em lugar disso, era nil. Uma vez
que a funo coluna-standard no tem quaisquer argumentos, a mensagem de erro poder ser difcil de compreender. No entanto, ela torna-se
compreensvel quando pensamos que o erro poder no ter ocorrido na invocao da funo coluna-standard mas sim em qualquer funo que
tenha sido invocada directa ou indirectamente por esta.
27

Consegue detect-lo?

87

Para se perceber melhor onde est o erro, o Visual Lisp providencia


algumas operaes extremamente teis. Uma delas d pelo nome de Error
Trace, est disponvel no menu View e destina-se a mostra a cascata
de invocaes que acabou por provocar o erro.28 Quando executamos essa
operao, o Visual Lisp apresenta-nos, numa pequena janela, a seguinte
informao:
<1>
[2]
[3]
[4]
[5]
[6]
...

:ERROR-BREAK
(+ nil -0.3)
(+XY (nil 0) -0.3 9)
(FUSTE (nil 0) 9 0.5 0.3)
(COLUNA (nil 0) 9 0.5 0.4 0.3 0.3 0.5)
(COLUNA-STANDARD)

Na informao acima, as reticncias representam outras invocaes que


no so relevantes para o nosso problema e que correspondem s funes
Auto Lisp cuja execuo antecede a das nossas funes.29 A listagem apresentada pelo Visual Lisp mostra, por ordem inversa, as invocaes de funes que provocaram o erro. Para cada linha, o Visual Lisp disponibiliza
um menu contextual que, entre outras operaes, permite visualizarmos
imediatamente no editor qual a linha do programa em questo.
A leitura da listagem do error trace diz-nos que o erro foi provocado
pela tentativa de somarmos nil a um nmero. Essa tentativa foi feita pela
funo +xy que foi invocada pela funo fuste que foi invocada pela funo coluna que, finalmente, foi invocada pela funo coluna-standard.
Como podemos ver, frente do nome de cada funo, aparecem os argumentos com que a funo foi invocada. Estes argumentos mostram que o
erro de que o Auto Lisp se queixa na soma, na realidade, foi provocado
muito antes disso, logo na invocao da funo coluna. De facto, visvel
que essa funo invocada com um ponto cuja coordenada x nil e no
um nmero como devia ser. Esta a pista que nos permite identificar o
erro: ele tem de estar na prpria funo coluna-standard e, mais especificamente, na expresso que cria as coordenadas que so passadas como
argumento da funo coluna. Se observarmos cuidadosamente essa expresso (que assinalmos a negrito) vemos que, de facto, est l um muito
subtil erro: o primeiro argumento da funo xy no zero mas sim a letra
maisculo.
28

A expresso error trace pode ser traduzida para Rastreio de erros.


Essas funes que foram invocadas antes das nossas revelam que, na verdade, parte da
funcionalidade do Visual Lisp est ela prpria implementada em Auto Lisp.
29

88

(defun coluna-standard ()
(coluna (xy O 0) 9 0.5 0.4 0.3 0.3 0.5))

Acontece que, em Auto Lisp, qualquer nome que no tenha sido previamente definido tem o valor nil e, como o nome constitudo pela letra O
no est definido, a avaliao do primeiro argumento da funo xy , na
verdade, nil. Esta funo, como se limita a fazer uma lista com os seus
argumentos, cria uma lista cujo primeiro elemento nil e cujo segundo
elemento zero. A lista resultante assim passada de funo em funo
at chegarmos funo +xy que, ao tentar fazer a soma, acaba por provocar o erro.
Esta sesso de depurao mostra o procedimento habitual para deteco de erros. A partir do momento em que o erro identificado, geralmente fcil corrigi-lo mas convm ter presente que o processo de identificao de erros pode ser moroso e frustrante. tambm um facto que
a experincia de deteco de erros extremamente til e que, por isso,
expectvel que enquanto essa experincia for reduzida, o processo de deteco seja mais lento.

Modelao Tridimensional

Como vimos na seco anterior, o AutoCad disponibiliza um conjunto de


operaes de desenho (linhas, rectngulos, crculos, etc) que nos permitem facilmente criar representaes bidimensionais de objectos, como sejam plantas, alados e cortes.
Embora at este momento apenas tenhamos utilizado as capacidades
de desenho bi-dimensional do AutoCad, possvel irmos mais longe, entrando no que se denomina por modelao tridimensional. Esta modelao
visa a representao grfica de linhas, superfcies e volumes no espao tridimensional.
Nesta seco iremos estudar as operaes do AutoCad que nos permitem modelar directamente os objectos tridimensionais.

5.1

Slidos Tridimensionais Pr-Definidos

As verses mais recentes do AutoCad disponibilizam um conjunto de operaes pr-definidas que constroem um slido a partir da especificao das
suas coordenadas tridimensionais. Embora as operaes pr-definidas apenas permitam construir um conjunto muito limitado de slidos, esse conjunto suficiente para a elaborao de modelos sofisticados.
89

As operaes pr-definidas para criao de slidos permitem construir


paralelippedos (comando box), cunhas (comando wedge), cilindros (comando cylinder), cones (comando cone), esferas (comando sphere),
toros (comando torus) e pirmides (comando pyramid). Os comandos
cone e pyramid permitem ainda a construo de troncos de cone e troncos
de pirmide atravs da utilizao do parmetro "Top". Cada um destes comandos aceita vrias opes que permitem construir slidos de diferentes
maneiras.30 A Figura 20 mostra um conjunto de slidos construdos pela
avaliao das seguintes expresses:31
(command "_.box" (xyz 1 1 1) (xyz 3 4 5))
(command "_.wedge" (xyz 4 2 0) (xyz 6 6 4))
(command "_.cone" (xyz 6 0 0) 1
"_Axis" (xyz 8 1 5))
(command "_.cone" (xyz 11 1 0) 2 "_Top" 1
"_Axis" (xyz 10 0 5))
(command "_.sphere" (xyz 8 4 5) 2)
(command "_.cylinder" (xyz 8 7 0) 1 "_Axis" (xyz 6 8 7))
(command "_.pyramid" "_Sides" 5 (xyz 1 6 1) (raio&angulo 1 0)
"_Axis" (xyz 2 7 9))
(command "_.torus" (xyz 13 6 5) 2 1)

Note-se que alguns dos slidos, nomeadamente o paralelippedo e a


cunha s podem ser construdos com a base paralela ao plano XY . Esta no
uma verdadeira limitao do AutoCad pois possvel alterar a orientao
do plano XY atravs da manipulao do sistema de coordenadas UCSuser
coordinate system.
A partir dos comandos disponibilizados pelo AutoCad possvel definir funes Auto Lisp que simplifiquem a sua utilizao. Embora o AutoCad permita vrios modos diferentes de se criar um slido, esses modos
esto mais orientados para facilitar a vida ao utilizador do AutoCad do que
propriamente para o programador de Auto Lisp. Para este ltimo, prefervel dispor de uma funo que, a partir de um conjunto de parmetros
simples, invoca o comando correspondente em AutoCad, especificando automaticamente as opes adequadas para a utilizao desses parmetros.
Para vermos um exemplo, consideremos a criao de um cilindro. Embora o AutoCad permita construir o cilindro de vrias maneiras diferentes,
30

Para se conhecer as especificidades de cada comando recomenda-se a consulta da documentao que acompanha o AutoCad.
31
A construo de uma pirmide exige a especificao simultnea do raio e ngulo da
base. Tal como explicado na seco 4.14, a funo raio&angulo constri essa especificao
a partir dos valores do raio e do ngulo.

90

Figura 20: Slidos primitivos em AutoCad.


cada uma empregando diferentes parmetros, podemos considerar que os
nicos parmetros relevantes so o centro da base, o raio da base e o centro
do topo. Estes parmetros so suficientes para especificar completamente
um cilindro, permitindo orient-lo no espao como muito bem entendermos. Assim, para o programador de Auto Lisp, basta-lhe invocar o comando AutoCad cylinder seleccionando o modo de construo que facilita o uso deste parmetros. precisamente isso que faz a seguinte funo:
(defun cilindro (centro-base raio centro-topo)
(command "_cylinder" centro-base raio "_Axis" centro-topo))

Usando esta funo agora mais simples definir outras estruturas mais
complexas. Por exemplo, uma cruz papal define-se pela unio de trs cilindros horizontais de comprimento progressivamente decrescente dispostos
ao longo de um cilindro vertical, tal como se pode ver na Figura 21. de
salientar que os cilindros tm todos o mesmo raio e que o seu comprimento
e posicionamento funo desse raio. Em termos de proporo, o cilindro
vertical da cruz papal tem um comprimento igual a 20 raios, enquanto que
os cilindros horizontais possuem comprimentos iguais a 14, 10 e 6 raios
91

Figura 21: Uma cruz papal.


e o seu eixo est posicionado a uma altura igual a 9, 13 e 17 raios. Estas
propores so implementadas pela seguinte funo:
(defun cruz-papal
(cilindro p
raio
(+xyz
(cilindro (+xyz
raio
(+xyz
(cilindro (+xyz
raio
(+xyz
(cilindro (+xyz
raio
(+xyz

(p raio)

p 0 0 (* 20 raio)))
p (* -7 raio) 0 (* 9 raio))
p (* +7 raio) 0 (* 9 raio)))
p (* -5 raio) 0 (* 13 raio))
p (* +5 raio) 0 (* 13 raio)))
p (* -3 raio) 0 (* 17 raio))
p (* +3 raio) 0 (* 17 raio))))

Uma das vantagens das representaes tri-dimensionais est no facto


de elas conterem toda a informao necessria para a gerao automtica
de vistas bi-dimensionais, incluindo as tradicionais projeces ortogonais.
Para isso, o AutoCad permite diferentes abordagens, desde a simples utilizao de mltiplas vistas (atravs do comando mview), cada uma com uma
92

Figura 22: Alado frontal, lateral e planta da cruz papal representada na


Figura 21.
perspectiva diferente, at a criao automtica de projeces e cortes (comandos solview, soldraw, flatshot e sectionplane, entre outros).
A Figura 22 mostra o alado frontal, o alado lateral e a planta produzidos
automaticamente pelo comando flatshot a partir do modelo apresentado na Figura 21.
Exerccio 5.1.1 Defina uma funo denominada prisma que cria um slido prismtico regular. A funo dever receber o nmero de lados do prisma, as coordenadas tridimensionais do centro da base do prisma, a distncia do centro da base a cada vrtice, o ngulo de
rotao da base do prisma e as coordenadas tridimensionais do centro do topo do prisma.
A ttulo de exemplo, considere as expresses
(prisma
(prisma
(prisma
(prisma
(prisma

3
5
4
6
7

(xyz 0 0
(xyz -2 0
(xyz 0 2
(xyz 2 0
(xyz 0 -2

0)
0)
0)
0)
0)

0.4
0.4
0.4
0.4
0.4

0
0
0
0
0

(xyz 0 0 5))
(xyz -1 1 5))
(xyz 1 1 5))
(xyz 1 -1 5))
(xyz -1 -1 5))

cuja avaliao produz a imagem seguinte:

93

5.2

Modelao de Colunas Dricas

A modelao tri-dimensional tem a virtude de nos permitir criar entidades


geomtricas muito mais realistas do que meros aglomerados de linhas a
representarem vistas dessas entidades. A ttulo de exemplo, reconsideremos a coluna Drica que apresentmos na seco 4.16. Nessa seco desenvolvemos um conjunto de funes cuja invocao criava uma vista frontal
dos componentes da coluna Drica. Apesar dessas vistas serem teis,
ainda mais til poder modelar directamente a coluna como uma entidade
tri-dimensional.
Nesta seco vamos empregar algumas das operaes mais relevantes
para a modelao tri-dimensional de colunas, em particular, a criao de
troncos de cone para modelar o fuste e o coxim e a criao de paralelippedos para modelar o baco.
Anteriormente, as nossas colunas estavam dispostas no plano x-y,
com as colunas a crescer ao longo do eixo dos y. Agora, ser apenas
a base das colunas que ficar assente no plano x-y: o corpo das colunas
ir desenvolver-se ao longo do eixo dos z. Embora fosse trivial empregar
outro arranjo dos eixos do sistema de coordenadas, este aquele que mais
prximo da realidade.
semelhana de inmeras outras operaes do AutoCad, cada uma
94

das operaes de modelao de slidos do AutoCad permite vrios modos


diferentes de invocao. No caso da operao de modelao de troncos
de coneconeo modo que nos mais conveniente aquele em que a
operao recebe as coordenadas do centro da base do cone e o raio dessa
base e, de seguida, especificamos o raio do topo do cone (com a opo
Top radius) e, finalmente, a altura do tronco.
Tendo isto em conta, podemos redefinir a operao que constri o fuste
tal como se segue:
(defun fuste (p a-fuste r-base r-topo)
(command "_.cone" p r-base
"_t" r-topo a-fuste))

Do mesmo modo, a operao que constri o coxim ficar com a forma:


(defun coxim (p a-coxim r-base r-topo)
(command "_.cone" p r-base
"_t" r-topo a-coxim))

Finalmente, no que diz respeito ao bacoo paralelippedo que colocado no topo da colunatemos vrias maneiras de o especificarmos. A
mais directa consiste em indicar os dois cantos do paralelippedo. Uma outra, menos directa, consiste em indicar o centro do paralelippedo seguido
do tamanho dos seus lados. Por agora, vamos seguir a mais directa:
(defun abaco (p a-abaco l-abaco)
(command "_.box"
(+xyz p (/ l-abaco -2.0) (/ l-abaco -2.0) 0)
(+xyz p (/ l-abaco +2.0) (/ l-abaco +2.0) a-abaco)))
Exerccio 5.2.1 Implemente a funo abaco mas empregando a criao de um paralelippedo centrado num ponto seguido da especificao do seu comprimento, largura e altura.

Finalmente, falta-nos implementar a funo coluna que, semelhana


do que fazia no caso bi-dimensional, invoca sucessivamente as funes
fuste, coxim e abaco mas, agora, elevando progressivamente a coordenada z:
(defun coluna (p
a-fuste r-base-fuste
a-coxim r-base-coxim
a-abaco l-abaco)
(fuste p a-fuste r-base-fuste r-base-coxim)
(coxim (+z p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
(abaco (+z p (+ a-fuste a-coxim)) a-abaco l-abaco))

95

Figura 23: Modelao tri-dimensional das variaes de colunas dricas.


Com estas redefinies, podemos agora repetir as colunas que desenhmos na seco 4.17 e que apresentmos na Figura 19, mas, agora, gerando
uma imagem tri-dimensional dessas mesmas colunas, tal como apresentamos na Figura 23:
(coluna
(coluna
(coluna
(coluna
(coluna
(coluna

(xyz 0 0
(xyz 3 0
(xyz 6 0
(xyz 9 0
(xyz 12 0
(xyz 15 0

0)
0)
0)
0)
0)
0)

9
7
9
8
5
6

0.5
0.5
0.7
0.4
0.5
0.8

0.4
0.4
0.5
0.3
0.4
0.3

0.3
0.6
0.3
0.2
0.3
0.2

0.3
0.6
0.2
0.3
0.1
0.4

1.0)
1.6)
1.2)
1.0)
1.0)
1.4)

A partir deste modelo, agora trivial usarmos as capacidades do AutoCad para extrair qualquer vista que pretendamos, incluindo perspectivas
como a que apresentamos na Figura 24.

Expresses Condicionais

Existem muitas operaes cujo resultado depende da realizao de um determinado teste. Por exemplo, a funo matemtica |x|que calcula o valor
absoluto de um nmeroequivale ao prprio nmero, se este positivo,
ou equivale ao seu simtrico se for negativo. Esta funo ter, portanto de
96

Figura 24: Perspectiva da modelao tri-dimensional das variaes de colunas dricas.


testar o seu argumento e escolher uma de duas alternativas: ou devolve o
prprio argumento, ou devolve o seu simtrico.
Estas expresses, cujo valor depende de um ou mais testes a realizar
previamente, permitindo escolher vias diferentes para a obteno do resultado, so designadas expresses condicionais.
No caso mais simples de uma expresso condicional, existem apenas
duas alternativas a seguir. Isto implica que o teste que necessrio realizar
para determinar a via de clculo a seguir deve produzir um de dois valores,
em que cada valor designa uma das vias.
Seguindo o mesmo racioccio, uma expresso condicional com mais de
duas alternativas dever implicar um teste com igual nmero de possveis
resultados. Uma expresso da forma caso o valor do teste seja 1, 2 ou 3,
o valor da expresso 10, 20 ou 30, respectivamente um exemplo desta
categoria de expresses que existe nalgumas linguagens (Basic, por exemplo). Contudo, estas expresses podem ser facilmente reduzidas primeira
atravs da decomposio da expresso condicional mltipla numa composio de expresses condicionais simples, em que o teste original tambm
decomposto numa srie de testes simples. Assim, poderiamos transformar
o exemplo anterior em: se o valor do teste 1, o resultado 10, caso contrrio, se o valor 2, o resultado 20, caso contrrio 30.

97

6.1

Expresses Lgicas

Desta forma, reduzimos todos os testes a expresses cujo valor pode ser
apenas um de dois, e a expresso condicional assume a forma de se . . . ento
. . . caso contrrio . . . . A expresso cujo valor usado para decidir se devemos usar o ramo ento ou o ramo caso contrrio denomina-se expresso
lgica e caracteriza-se por o seu valor ser interpretado como verdade ou falso.
Por exemplo, a expresso lgica (> x y) testa se o valor de x maior que
o valor de y. Se for, a expresso avalia para verdade, caso contrrio avalia
para falso.

6.2

Valores Lgicos

Algumas linguagens de programao consideram a verdade e o falso como


dois elementos de um tipo especial de dados denominado lgico ou booleano.32 Outras linguagens, como o Lisp, entendem que o facto de se considerar um valor como verdadeiro ou falso no implica necessariamente
que o valor tenha de ser de um tipo de dados especial, mas apenas que a
expresso condicional considera alguns dos valores como representando o
verdadeiro e os restantes como o falso.
Em Lisp, as expresses condicionais consideram como falso um nico
valor. Esse valor representado por nil, o mesmo valor que usmos anteriormente para representar uma lista vazia. Qualquer outro valor diferente
de nil considerado como verdadeiro. Assim, do ponto de vista de uma
expresso condicional, o nmero 123, por exemplo, um valor verdadeiro.
Contudo, no faz muito sentido para o utilizador humano considerar um
nmero como verdadeiro ou falso, pelo que se introduziu uma constante
na linguagem para representar verdade. Essa constante representa-se por
t. A lgica que se t diferente de nil e se nil o nico valor que
representa a falsidade, ento t representa necessariamente a verdade.

6.3

Predicados

No caso mais usual, uma expresso lgica uma invocao de funo com
determinados argumentos. Nesta situao, a funo usada como teste
denominada predicado e o valor do teste interpretado como sendo verdadeiro ou falso. O predicado , consequentemente, uma funo que devolve
apenas verdade ou falso.
32

De George Boole, matemtico ingls e inventor da lgebra da verdade e da falsidade.

98

Apesar da adopo dos smbolos t e nil, convm alertar que nem todos os predicados devolvem t ou nil exclusivamente. Alguns h que,
quando querem indicar verdade, devolvem valores diferentes de t (e de
nil, obviamente).

6.4

Predicados Aritmticos

Os operadores relacionais matemticos <, >, =, , e 6= so um dos exemplos mais simples de predicados. Estes operadores comparam nmeros
entre si e permitem saber se um nmero menor que outro. O seu uso em
Lisp segue as regras da notao prefixa e escrevem-se, respectivamente, <,
>, =, <=, >= e /=. Eis alguns exemplos:
_$ (> 4 3)
t
_$ (< 4 3)
nil
_$ (<= (+ 2 3) (- 6 1))
t

6.5

Operadores Lgicos

Para se poder combinar expresses lgicas entre si existem os operadores


and, or e not. O and e o or recebem qualquer nmero de argumentos. O
not s recebe um. O valor das combinaes que empregam estes operadores lgicos determinado do seguinte modo:
O and avalia os seus argumentos da esquerda para a direita at que
um deles seja falso, devolvendo este valor. Se nenhum for falso o and
devolve verdade.
O or avalia os seus argumentos da esquerda para a direita at que um
deles seja verdade, devolvendo este valor. Se nenhum for verdade o
or devolve falso.
O not avalia para verdade se o seu argumento for falso e para falso
em caso contrrio.
Note-se que embora o significado de falso seja claro pois corresponde
necessariamente ao valor nil, o significado de verdade j no to claro
pois, desde que seja diferente de nil, considerado verdade.
Exerccio 6.5.1 Qual o valor das seguintes expresses?

99

1. (and (or (> 2 3) (not (= 2 3))) (< 2 3))


2. (not (or (= 1 2) (= 2 3)))
3. (or (< 1 2) (= 1 2) (> 1 2))
4. (and 1 2 3)
5. (or 1 2 3)
6. (and nil 2 3)
7. (or nil nil 3)

6.6

Predicados com nmero varivel de argumentos

Uma propriedade importante dos predicados aritmticos <, >, =, <=, >= e
/= aceitarem qualquer nmero de argumentos. No caso em que h mais
do que um argumento, o predicado aplicado sequencialmente aos pares
de argumentos. Assim, (< e1 e2 e3 ... en1 en ) equivalente a escrever (and (< e1 e2 ) (< e2 e3 ) ... (< en1 en )). Este comportamento
visvel nos sequintes exemplos.
_$ (< 1 2 3)
T
_$ (< 1 2 2)
nil

Um caso particular que convm ter em ateno que embora a expresso (= e1 e2 ... en ) teste se os elementos e1 e2 . . . en so todos iguais,
(/= e1 e2 ... en ) no testa se os elementos e1 e2 . . . en so todos diferentes: uma vez que o teste aplicado sucessivamente a pares de elementos
perfeitamente possvel que existam dois elementos iguais desde que no
sejam consecutivos. Esse comportamento visvel no seguinte exemplo:33
_$ (/= 1 2 3)
T
_$ (/= 1 2 1)
T

6.7

Predicados sobre Cadeias de Caracteres

Na verdade, os operadores <, >, =, <=, >= e /= no se limitam a operarem


sobre nmeros: eles aceitam tambm cadeias de caracteres como argumentos, fazendo uma comparao lexicogrfica: os argumentos so comparados
33

Outros dialectos de Lisp apresentam um comportamento diferente para esta operao.


Em Common Lisp, por exemplo, o predicado /= testa se, de facto, os argumentos so todos
diferentes.

100

carcter a carcter enquanto forem iguais. Quando so diferentes, a ordem


lxicografica do primeiro carcter diferente nas duas strings determina o
valor lgico da relao. Se a primeira string acabar antes da segunda,
considera-se que menor, caso contrrio, maior.
_$ (=
T
_$ (=
nil
_$ (<
T
_$ (<
T

6.8

"pois" "pois")
"pois" "poisar")
"pois" "poisar")
"abcd" "abce")

Predicados sobre Smbolos

Para alm de nmeros e strings, o predicado = permite ainda comparar


smbolos.
_$ (= pois pois)
T
_$ (= pois poisar)
nil

Nenhum dos outros operadores relacionais aplicvel a smbolos.

6.9

Predicados sobre Listas

Vimos que os dados manipulados pelo Lisp se podiam classificar em atmicos ou no-atmicos consoante era impossvel, ou no, aplicar-lhes as
operaes de listas car e cdr.
Para facilitar a identificao das entidades atmicos, a linguagem Lisp
disponibiliza uma funo denominada atom que s verdadeira para as
entidades atmicas:
_$ (atom
T
_$ (atom
T
_$ (atom
T
_$ (atom
nil
_$ (atom
nil

1)
"dois")
quatro)
(cons 1 "dois"))
(list 1 "dois" 3.0))

101

_$ (atom ())
T
_$ (atom t)
T
_$ (atom nil)
T

A funo atom permite-nos, assim, saber quais os tipos de entidades a


que podemos aplicar as operaes car e cdr: apenas aquelas que no so
tomos.

6.10

Reconhecedores

Para alm dos operadores relacionais, existem muitos outros predicados


em Lisp, como por exemplo o zerop que testa se um nmero zero:
_$ (zerop 1)
nil
_$ (zerop 0)
t

O facto de zerop terminar com a letra p deve-se a uma conveno


adoptada em Lisp segundo a qual os predicados cujo nome seja uma ou
mais palavras devem ser distinguidos das restantes funes atravs da concatenao da letra p (de Predicate) ao seu nome. Infelizmente, por motivos histricos, nem todos os predicados pr-definidos seguem esta conveno. No entanto, nos predicados que definirmos devemos ter o cuidado de
seguir esta conveno.
Note-se que o operador zerop serve para reconhecer um elemento em
particular (o zero) de um tipo de dados (os nmeros). Este gnero de predicados denominam-se de reconhecedores. Um outro exemplo de um reconhecedor o predicado null que reconhece a lista vazia. A funo null
devolve verdade quando aplicada a uma lista vazia e falso em qualquer
outro caso:
_$ (list 1 2 3)
(1 2 3)
_$ (null (list 1 2 3))
nil
_$ (list)
nil
_$ (null (list))
T

102

Para se perceberem as duas ltimas expresses do exemplo anterior


convm recordar que nil representa no s a falsidade mas tambm a lista
vazia.

6.11

Reconhecedores Universais

Um outro conjunto importante de predicados so os denominados reconhecedores universais. Estes no reconhecem elementos particulares de um tipo
de dados mas sim todos os elementos de um particular tipo de dados. Um
reconhecedor universal aceita qualquer tipo de valor como argumento e
devolve verdade se o valor do tipo pretendido.
Por exemplo, para sabermos se uma determinada entidade um nmero podemos empregar o predicado numberp:34
_$ (numberp 1)
T
_$ (numberp nil)
nil
_$ (numberp "Dois")
nil

Para testar se uma determinada entidade uma lista podemos empregar o predicado listp. Este predicado verdade para qualquer lista (incluindo a lista vazia) e falso para tudo o resto:
_$ (listp
T
_$ (listp
nil
_$ (listp
T
_$ (listp
T

(list 1 2))
1)
(list))
(cons 1 2))

Note-se, no ltimo exemplo, que o reconhecedor universal listp tambm considera como lista um mero par de elementos. Na realidade, este
reconhecedor devolve verdade sempre que o seu argumento um dotted
pair ou o nil. As listas, como vimos, no so mais do que arranjos particulares de dotted pairs ou, no limite, uma lista vazia.
34

tradicional, em vrios dialectos de Lisp, terminar o nome dos predicados com a letra
p. Noutros dialectos, prefere-se terminar esse nome com um ponto de interrogao precisamente porque, na prtica, a invocao de um predicado corresponde a uma pergunta que
fazemos. Neste texto, nos casos em que fizer sentido enquadrarmo-nos com a tradio, iremos empregar a letra p; em todos os outros casos iremos empregar o ponto de interrogao.

103

Para a maioria dos tipos, o Auto Lisp no providencia nenhum reconhecedor universal, antes preferindo usar a funo genrica type que devolve
o nome do tipo como smbolo. Como vimos na secco 3.14, temos:
_$ (type
REAL
_$ (type
INT
_$ (type
STR
_$ (type
USUBR
_$ (type
SUBR

pi)
1)
"Ola")
quadrado)
+)

Obviamente, nada nos impede de definir os reconhecedores universais


que pretendermos. semelhana do predicado numberp podemos definir
os seus subcasos integerp e realp:
(defun integerp (obj)
(= (type obj) int))
(defun realp (obj)
(= (type obj) real))

igualmente trivial definir um reconhecedor universal de strings e outro para funes (compiladas ou no):
(defun stringp (obj)
(= (type obj) str))
(defun functionp (obj)
(or (= (type obj) subr)
(= (type obj) usubr)))

6.12

Exerccios

Exerccio 6.12.1 O que uma expresso condicional? O que uma expresso lgica?
Exerccio 6.12.2 O que um valor lgico? Quais so os valores lgicos empregues em Auto
Lisp?
Exerccio 6.12.3 O que um predicado? D exemplos de predicados em Auto Lisp.
Exerccio 6.12.4 O que um operador relacional? D exemplos de operadores relacionais
em Auto Lisp.

104

Exerccio 6.12.5 O que um operador lgico? Quais so os operadores lgicos que conhece
em Auto Lisp?
Exerccio 6.12.6 O que um reconhecedor? O que um reconhecedor universal? D exemplos em Auto Lisp.
Exerccio 6.12.7 Traduza para Lisp as seguintes expresses matemticas:
1. x < y
2. x y
3. x < y y < z
4. x < y x < z
5. x y z
6. x y < z
7. x < y z

Estruturas de Controle

At agora, temos visto essencialmente a definio e aplicao de funes.


No caso da aplicao de funes, vimos que o Lisp avalia todos os elementos da combinao e, em seguida, invoca a funo que o resultado da
avaliao do primeiro elemento da combinao usando, como argumentos,
o resultado da avaliao dos restantes elementos. Este comportamento
imposto pela linguagem e um exemplo de uma estrutura de controle.
Em termos computacionais, o controle est associado ordem pela qual
o computador executa as instrues que lhe damos. Se nada for dito em
contrrio, o computador executa sequencialmente as instrues que lhe damos. No entanto, algumas dessas instrues podem provocar o salto de instrues, i.e., podem forar o computador a continuar a executar instrues
mas a partir de outro ponto do programa.
Com o tempo, os programadores aperceberam-se que a utilizao indiscriminada de saltos tornava os programas extremamente difceis de compreender por um ser humano e, por isso, inventaram-se formas mais estruturadas de controlar o computador: as estruturas de controle.
As estruturas de controle no so mais do que formas padronizadas de
se controlar um computador. Esta estruturas de controle no so disponibilizadas pelo computador em si mas sim pela linguagem de programao
que estivermos a utilizar que, depois, as converte nas estruturas de controle
bsicas do computador.
Iremos agora ver algumas das estruturas de controle mas importantes.
105

7.1

Sequenciao

A estrutura de controle mais simples que existe a sequenciao.


Na estrutura de controle de sequenciao, o Lisp avalia uma sequncia
de expresses uma a seguir outra, devolvendo o valor da ltima. Logicamente, se apenas o valor da ltima expresso utilizado, ento os valores
de todas as outras avaliaes dessa sequncia so descartados e estas apenas so relevante pelos efeitos secundrios que possam provocar.
Como j vimos, possvel usar sequenciao na definio de uma funo. Esta funo, quando invocada, ir sucessivamente avaliar cada uma
das expresses, descartando o seu valor, at chegar ltima que ser avaliada e o seu valor ser considerado como o valor da invocao da funo.
Aquando da definio da coluna drica, vimos que a funo coluna
usava sequenciao para desenhar, sucessivamente, o fuste, o coxim e o
baco:
(defun coluna (p
a-fuste r-base-fuste
a-coxim r-base-coxim
a-abaco l-abaco)
(fuste p a-fuste r-base-fuste r-base-coxim)
(coxim (+z p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
(abaco (+z p (+ a-fuste a-coxim)) a-abaco l-abaco))

Como se pode ver, a funo recebe vrios parmetros necessrios para


especificar completamente as caractersticas geomtricas da coluna.
Quando invocada, a funo coluna limita-se a, primeiro, invocar a
funo fuste, passando-lhe os argumentos relevantes, segundo, invocar a
funo coxim, passando-lhe os argumentos relevantes e, terceiro, invocar
a funo abaco, passando-lhe os argumentos relevantes. precisamente
este primeiro, segundo, terceiro que caracteriza a estrutura de controle de sequenciao: as avaliaes so feitas sequencialmente.
Se tivermos em conta que uma funo, quando invocada, precisa de
computar e devolver um valor, a presena de sequenciao levanta a pergunta sobre qual dos valores que foram sucessivamente computados que
representa o valor da funo invocada: por convenincia, Lisp arbitra que
o ltimo e todos os anteriores so descartados.
Embora o corpo de uma funo possa conter vrias expresses que,
quando a funo invocada, so avaliadas sequencialmente, na realidade,
a sequenciao implementada em Lisp atravs de um operador denominado progn:

106

_$ (progn
(+ 1 2)
(* 3 4))
12

Este operador recebe vrias expresses como argumento que avalia sequencialmente, devolvendo o valor da ltima. Quando definimos uma funo todas as expresses que colocamos no corpo da funo so avaliadas
num progn implcito. Como iremos ver, existem ainda outras formas da
linguagem Lisp que possuem progns implcitos.35

7.2

Invocao de Funes

A invocao de uma funo a estrutura de controle mais usada em Lisp.


Para se invocar uma funo, necessrio construir uma combinao
cujo primeiro elemento seja uma expresso que avalia para a funo que
se pretende invocar e cujos restantes elementos so expresses que avaliam para os argumentos que se pretende passar funo. O resultado da
avaliao da combinao o valor calculado pela funo para aqueles argumentos.
A avaliao de uma combinao deste gnero processa-se nos seguintes
passos:
1. Todos os elementos da combinao so avaliados, sendo que o valor
do primeiro elemento necessariamente uma funo.
2. Associam-se os parmetros formais dessa funo aos argumentos,
i.e., aos valores dos restantes elementos da combinao. Cada parmetro associado a um argumento, de acordo com a ordem dos
parmetros e argumentos. gerado um erro sempre que o nmero
de parmetros no igual ao nmero de argumentos.
3. Avalia-se o corpo da funo tendo em conta estas associaes entre os
parmetros e os argumentos.
35

O nome progn parece pouco intuitivo mas, como a maioria dos nomes usados em
Lisp, tem uma histria que o justifica. Os Lisps originais disponibilizavam um operador
denominado prog (abreviatura de program) que, entre vrias outras coisas, permitia
sequenciao. Para alm deste operador, foram introduzidas variantes mais simples, denominadas prog1que avaliava sequencialmente os seus argumentos e retornava o valor da
primeira, prog2que avaliava sequencialmente os seus argumentos e retornava o valor
da segundae, finalmente, prognque avaliava sequencialmente os seus argumentos e
retornava o valor da ltima. Apenas este ltimo operador foi implementado em Auto Lisp.

107

Para exemplificar, consideremos a definio da funo quadrado e a


seguinte combinao:
_$ (defun quadrado (x) (* x x))
QUADRADO
_$ (+ 1 (quadrado 2) 3)

Para que o avaliador de Lisp consiga avaliar a ltima combinao necessita de comear por avaliar todos os seus elementos. O primeiro, o
smbolo +, avalia para o procedimento que realiza somas mas, antes de
poder fazer a soma, precisa de determinar os seus argumentos atravs da
avaliao dos restantes elementos da combinao. O primeiro argumento
o nmero 1 pois, como vimos anteriormente, o valor de um nmero
o prprio nmero. Depois de avaliar o primeiro argumento, o avaliador
depara-se com uma nova combinao. Nesse momento, suspende a avaliao que estava a fazer e inicia uma nova avaliao, agora para a combinao
(quadrado 2). Novamente, porque se trata da aplicao de uma funo,
o avaliador vai avaliar todos os seus argumentos que, neste caso, apenas
um nmero que avalia para ele prprio. A partir deste momento, o controle
transferido para o corpo da funo quadrado, mas fazendo a associao
do parmetro x ao valor 2. A avaliao do corpo da funo quadrado implica a avaliao da combinao (* x x). Trata-se de uma multiplicao
cujos argumentos so obtidos pela repetida avaliao do smbolo x. Uma
vez que a invocao da funo tinha associado x ao argumento 2, o valor
de x 2. No entanto, para realizar a multiplicao necessrio ter em conta
que se trata, mais uma vez, da invocao de uma funo, pelo que a avaliao da invocao da funo quadrado por sua vez suspensa para se
passar avaliao da multiplicao. No instante seguinte, a multiplicao
realizada e tem como resultado o nmero 4. Nesse momento, o avaliador
termina a invocao da multiplicao e faz o controle regressar avaliao do quadrado de 2. Uma vez que a multiplicao a ltima expresso
da funo quadrado, esta invocao vai tambm terminar tendo como resultado o mesmo da multiplicao, i.e., 4. Mais uma vez, o avaliador vai
transferir o controle, agora para a expresso que estava a avaliar quando
invocou a funo quadrado, avaliando o ltimo argumento da soma (que
avalia para ele prprio), permitindo-lhe assim concluir a soma com o valor
8.
Resumidamente, a invocao de funes consiste em suspender a
avaliao que se estava a fazer para (1) se associarem os argumentos da invocao aos parmetros da funo invocada, (2) avaliar o corpo da funo
tendo aquela associao em conta e (3) retomar a avaliao suspendida
108

usando como valor da invocao o valor da ltima expresso do corpo da


funo invocada.
As caractersticas da linguagem Lisp tornam-na particularmente apta
definio e invocao de funes. De facto, as bases matemticas da linguagem Lisp assentam precisamente sobre o conceito de funo. Em Lisp, as
funes usam-se no s por uma questo de convenincia mas tambm por
uma questo de modelao: uma funo representa algo que devemos tornar
claro. O exemplo anterior pode ser reescrito para a forma (+ 1 (* 2 2) 3)
mas perdeu-se o conceito de quadrado que estava anteriormente evidente.
Em geral, quando usamos uma funo estamos a exprimir uma dependncia entre entidades. Uma dessas entidades diz-se ento funo das restantes e o seu valor determinado pela invocao de uma funo que recebe as restantes entidades como argumentos.

7.3

Variveis Locais

Consideremos o seguinte tringulo

a
e tentemos definir uma funo Auto Lisp que calcula a rea do tringulo a
partir dos parmetros a, b e c.
Uma das formas de calcular a rea do tringulo baseia-se na famosa
frmula de Heron:36
A=

s (s a) (s b) (s c)

em que s o semi-permetro do tringulo:


s=

a+b+c
2

36

Heron de Alexandria foi um importante matemtico e engenheiro Grego do primeiro


sculo depois de Cristo a quem se atribuem inmeras descobertas e invenes, incluindo a
mquina a vapor e a seringa.

109

Ao tentarmos usar a frmula de Heron para definir a correspondente


funo Auto Lisp, somos confrontados com uma dificuldade: a frmula
est escrita (tambm) em termos do semi-permetro s mas s no um parmetro, um valor derivado dos parmetros do tringulo.
Uma das maneira de resolvermos este problema consiste em eliminar a
varivel s, substituindo-a pelo seu significado:
s




a+b+c a+b+c
a+b+c
a+b+c
A=
a
b
c
2
2
2
2
A partir desta frmula j possvel definir a funo pretendida:
(defun area-triangulo (a b
(sqrt (* (/ (+ a b c) 2)
(- (/ (+ a b c)
(- (/ (+ a b c)
(- (/ (+ a b c)

c)
2) a)
2) b)
2) c))))

Infelizmente, esta definio tem dois problemas. O primeiro que se


perdeu a correspondncia entre a frmula original e a definio da funo,
tornando mais difcil reconhecer que a funo est a implementar a frmula
de Heron. O segundo problema que a funo obrigada a repetidamente
empregar a expresso (/ (+ a b c) 2), o que no s representa um desperdcio do nosso esforo, pois tivemos de a escrever quatro vezes, como
representa um desperdcio de clculo, pois a expresso calculada quatro
vezes, embora saibamos que ela produz sempre o mesmo resultado.
Para resolver estes problemas, o Auto Lisp permite a utilizao de variveis locais. Uma varivel local uma varivel que apenas tem significado dentro de uma funo e que usada para calcular valores intermdios
como o do semi-permetro s. Empregando uma varivel local, podemos reescrever a funo area-triangulo da seguinte forma:
(defun area-triangulo (a b c / s)
(setq s (/ (+ a b c) 2))
(sqrt (* s (- s a) (- s b) (- s c))))

Na definio anterior h dois aspectos novos que vamos agora discutir: a declarao da varivel local s e a atribuio dessa varivel atravs do
operador setq.
Para se indicar que uma funo vai usar variveis locais, estas tm de
ser declaradas conjuntamente com a lista de parmetros da funo. Para
isso, vamos agora ampliar a sintaxe da definio de funes que apresentmos anteriormente (na seco 3.6 de modo a incorporar a declarao de
variveis locais:
110

(defun nome (parmetro1 ... parmetron


/ varivel1 ... varivelm )
corpo)

Note-se que as variveis locais so separadas dos parmetros por uma


barra. necessrio ter o cuidado de no colar a barra nem ao ltimo
parmetro nem primeira varivel sob pena de o Auto Lisp considerar a
barra como parte de um dos nomes (e, consequentemente, tratar as variveis locais como se de parmetros se tratasse).
Embora seja raro, perfeitamente possvel termos funes sem parmetros mas com variveis locais. Neste caso, a sintaxe da funo seria simplesmente:
(defun nome (/ varivel1 ... varivelm )
corpo)

7.4

Atribuio

Para alm da declarao de uma varivel local ainda necessrio atribuirlhe um valor, i.e., realizar uma operao de atribuio. Para isso, o Auto
Lisp disponibiliza o operador setq, cuja sintaxe :
(setq varivel1 expresso1
...
varivelm expressom )

A semntica deste operador consiste simplesmente em avaliar a expresso expresso1 e associar o seu valor ao nome varivel1 , repetindo este
processo para todas as restantes variveis e valores.
A utilizao que fazemos deste operador na funo area-triangulo
fcil de compreender:
(defun area-triangulo (a b c / s)
(setq s (/ (+ a b c) 2))
(sqrt (* s (- s a) (- s b) (- s c))))

Ao invocarmos a funo area-triangulo, passando-lhe os argumentos correspondentes aos parmetros a, b e c, ela comea por introduzir um
novo nomesque vai existir apenas durante a invocao da funo. De
seguida, atribui a esse nome o valor que resultar da avaliao da expresso (/ (+ a b c) 2). Finalmente, avalia as restantes expresses do corpo
da funo. Na prtica, como se a funo dissesse: Sendo s = a+b+c
2 ,
p
calculemos s (s a) (s b) (s c).
111

H dois detalhes que importante conhecer relativamente s variveis


locais. O primeiro detalhe, que iremos discutir mais em profundidade na
prxima seco, que se nos esquecermos de declarar a varivel, ela ser
tratada como varivel global. O segundo detalhe que se nos esquecermos
de atribuir a varivel ela fica automaticamente com o valor nil. Logicamente, para que uma varivel nos seja til, devemos mudar o seu valor
para algo que nos interesse.
Para visualizarmos um exemplo mais complexo, consideremos a equao do segundo grau
ax2 + bx + c = 0
Como sabemos, esta equao tem duas razes que se podem obter pela famosa frmula resolvente:

b + b2 4ac
b b2 4ac
x=
x=
2a
2a
Dada a utilidade desta frmula, podemos estar interessados em definir
uma funo que, dados os coeficientes a, b e c da equao do segundo grau
nos devolve as suas duas raizes. Para isso, vamos devolver um par de
nmeros calculados usando as duas frmulas acima:
(defun raizes-equacao-segundo-grau (a b c)
(cons (/ (- (- b)
(sqrt (- (quadrado b) (* 4 a c))))
(* 2 a))
(/ (+ (- b)
(sqrt (- (quadrado b) (* 4 a c))))
(* 2 a))))

Por exemplo, usando a definio anterior, podemos calcular as solues


da equao (x 2)(x 3) = 0. De facto, esta equao equivalente a
x2 5x + 6 = 0, i.e., basta calcularmos as razes empregando a frmula
resolvente com os coeficientes a = 1, b = 5, c = 6, ou seja:
_$ (raizes-equacao-segundo-grau 1 -5 6)
(2.0 . 3.0)

Aparentemente, tudo est bem. Contudo, h uma ineficincia bvia:


a funo raizes-equacao-segundo-grau
calcula duas vezes o mesmo

valor correspondente ao termo b2 4ac. Isso perfeitamente visvel na


dupla ocorrncia da expresso (sqrt (- (quadrado b) (* 4 a c))).

112

Mais uma vez, atravs do uso de variveis locais e do operador setq


que vamos simplificar etornar mais eficiente esta funo. Para isso, bastanos calcular o valor de b2 4ac, associando-o a uma varivel local e, em
seguida, usamos esse valor nos dois locais em que necessrio. Do mesmo
modo, podemos declarar uma varivel para conter o valor da expresso 2a
e outra para conter b. O resultado fica ento:
(defun raizes-equacao-segundo-grau (a b c
/ raiz-b2-4ac 2a -b)
(setq raiz-b2-4ac (sqrt (- (quadrado b) (* 4 a c)))
2a (* 2 a)
-b (- b))
(cons (/ (- -b raiz-b2-4ac)
2a)
(/ (+ -b raiz-b2-4ac)
2a)))

importante salientar que raiz-b2-4ac, 2a e -b so apenas nomes


associados a nmeros que resultaram da avaliao das expresses correspondentes. Aps estas atribuies, quando se vai avaliar a expresso que
constri o par, esses nomes vo ser por sua vez avaliados e, logicamente,
vo ter como valor a atribuio que lhes tiver sido feita anteriormente.
Um outro ponto importante a salientar que aqueles nomes s existem
durante a invocao da funo e, na realidade, existem diferentes ocorrncias daqueles nomes para diferentes invocaes da funo. Este ponto de
crucial importncia para se perceber que, aps a invocao da funo, os
nomes, quer de parmetros, quer de variveis locais, desaparecem, conjuntamente com as associaes que lhes foram feitas.
Exerccio 7.4.1 Como viu, as variveis locais permitem evitar clculos repetidos. Uma outra forma de evitar esses clculos repetidos atravs do uso de funes auxiliares. Redefina a funo raizes-equacao-segundo-grau de forma a que no seja necessrio usar
quaisquer variveis locais.

7.5

Variveis Globais

Qualquer referncia a um nome que no est declarado localmente implica


que esse nome seja tratado como uma varivel global.37 O nome pi, por
exemplo, representa a varivel pi e pode ser usado em qualquer ponto dos
nossos programas. Por esse motivo, o nome pi designa uma varivel global.
37

Na realidade, o Auto Lisp permite ainda uma terceira categoria de nomes que no so
nem locais, nem globais. Mais frente discutiremos esse caso.

113

A declarao de variveis globais mais simples que a das variveis locais: basta uma primeira atribuio, em qualquer ponto do programa, para
a varivel ficar automaticamente declarada. Assim, se quisermos introduzir uma nova varivel global, por exemplo, para definir a razo de ouro38

1+ 5
=
1.6180339887
2
basta-nos escrever:
(setq razao-de-ouro (/ (+ 1 (sqrt 5)) 2))

A partir desse momento, o nome razao-de-ouro pode ser referenciado em qualquer ponto dos programas.
importante referir que o uso de variveis globais deve ser restrito,
na medida do possvel, definio de constantes, i.e., variveis cujo valor
nunca muda como, por exemplo, pi. Outros exemplos que podem vir a ser
teis incluem 2*pi, pi/2, 4*pi e pi/4, bem como os seus simtricos, que
se definem custa de:
(setq 2*pi (* 2 pi))
(setq pi/2 (/ pi 2))
(setq 4*pi (* 4 pi))
(setq pi/4 (/ pi 4))
(setq -pi (- pi))
(setq -2*pi (- 2*pi))
(setq -pi/2 (- pi/2))
(setq -4*pi (- 4*pi))
(setq -pi/4 (- pi/4))
38

Tambm conhecida por proporo divina e nmero de ouro, entre outras designaes, e
abreviada por em homenagem a Fineas, escultor Grego responsvel pela construo do
Partnon onde, supostamente, usou esta proporo. A razo de ouro foi inicialmente introduzida por Euclides quando resolveu o problema de dividir um segmento de recta em
duas partes de tal forma que a razo entre o segmento de recta e a parte maior fosse igual
razo entre a parte maior e a menor. Se for a o comprimento do parte maior e b o da menor,
o problema de Euclides idntico a dizer que a+b
= ab . Daqui resulta que a2 ab b2 = 0
a

b b2 +4b2
ou que a =
= b 12 5 . A raiz que faz sentido , obviamente, a = b 1+2 5 e,
2

consequentemente, a razo de ouro = ab = 1+2 5 .

114

O facto de uma varivel global ser uma constante implica que a varivel atribuda uma nica vez, no momento da sua definio. Esse facto
permite-nos usar a varivel sabendo sempre qual o valor a que ela est associada. Infelizmente, por vezes necessrio usarmos variveis globais que
no so constantes, i.e., o seu valor muda durante a execuo do programa,
por aco de diferentes atribuies feitas em locais diferentes e em momentos diferentes. Quando temos variveis globais que so atribuidas em diversos pontos do nosso programa, o comportamento deste pode tornar-se
muito mais difcil de entender pois, na prtica, pode ser necessrio compreender o funcionamento de todo o programa em simultneo e no apenas
funo a funo, como temos feito. Por este motivo, devemos evitar o uso
de variveis globais que sejam atribuidas em vrios pontos do programa.

7.6

Variveis Indefinidas

A linguagem Auto Lisp difere da maioria dos outros dialecto de Lisp no


tratamento que d a variveis indefinidas, i.e., variveis a que nunca foi atribudo um valor.39 Em geral, as linguagens de programao consideram que
a avaliao de uma varivel indefinida um erro. No caso da linguagem
Auto Lisp, a avaliao de qualquer varivel indefinida simplesmente nil.
A consequncia deste comportamento que existe o potencial de os
erros tipogrficos passarem despercebidos, tal como demonstrado no seguinte exemplo:
_$ (setq minha-lista (list 1 2 3))
(1 2 3)
_$ (cons 0 mimha-lista)
(0)

Reparemos, no exemplo anterior, que a varivel que foi definida no


a mesma que est a ser avaliada pois a segunda tem uma gralha no nome.
Em consequncia, a expresso que tenta juntar o 0 ao nicio da lista (1 2 3)
est, na realidade, a juntar o 0 ao nicio da lista nil que, como sabemos,
tambm a lista vazia.
Este gnero de erros pode ser extremamente difcil de detectar e, por
isso, a maioria das linguagens abandonou este comportamento. O Auto
Lisp, por motivos histricos, manteve-o, o que nos obriga a termos de ter
muito cuidado para no cometermos erros que depois passem indetectados.
39

Na terminologia original do Lisp, estas variveis diziam-se unbound.

115

Figura 25: Perspectiva da modelao tri-dimensional de colunas cujos parmetros foram escolhidos aleatoriamente. Apenas uma das colunas obedece
aos cnones da Ordem Drica.

7.7

Propores de Vitrvio

A modelao de colunas dricas que desenvolvemos na seco 5.2 permitenos facilmente construir colunas, bastando para isso indicarmos os valores
dos parmetros relevantes, como a altura e o raio da base do fuste, a altura e raio da base do coxim e a altura e largura do baco. Cada um destes
parmetros constitui um grau de liberdade que podemos fazer variar livremente.
Embora seja lgico pensar que quantos mais graus de liberdade tivermos mais flexvel a modelao, a verdade que um nmero excessivo
de parmetros pode conduzir a modelos pouco realistas. Esse fenmeno
evidente na Figura 25 onde mostramos uma perspectiva de um conjunto
de colunas cujos parmetros foram escolhidos aleatoriamente.
Na verdade, de acordo com os cnones da Ordem Drica, os diversos
parmetros que regulam a forma de uma coluna devem relacionar-se entre
si segundo um conjunto de propores bem definidas. Vitrvio40 , no seu
famoso tratado de arquitectura, considera que essas propores derivam
das propores do prprio ser humano:
40

Vitrvio foi um escritor, arquitecto e engenheiro romano que viveu no sculo um antes
de Cristo e autor do nico tratado de arquitectura que sobreviveu a antiguidade.

116

Uma vez que pretendiam erguer um templo com colunas


mas no tinham conhecimento das propores adequadas, [. . . ]
mediram o comprimento dum p de um homem e viram que
era um sexto da sua altura e deram coluna uma proporo
semelhante, i.e., fizeram a sua altura, incluindo o capitel, seis
vezes a largura da coluna medida na base. Assim, a ordem Drica obteve a sua proporo e a sua beleza, da figura masculina.
Mais concretamente, Vitrvio caracteriza as colunas da Ordem Drica
em termos do conceito de mdulo:
A largura das colunas, na base, ser de dois modulos e a sua altura, incluindo
os capitis, ser de catorze.
Daqui se deduz um mdulo iguala o raio da base da coluna e que a
altura da coluna dever ser 14 vezes esse raio. Dito de outra forma, o
1
raio da base da coluna dever ser 14
da altura da coluna.
A altura do capitel ser de um mdulo e a sua largura de dois mdulos e um
sexto.
Isto implica que a altura do coxim somado do baco ser um mdulo, ou seja, igual ao raio da base da coluna e a largura do baco ser
de 2 61 mdulos ou 13
6 do raio. Juntamente com o facto de a altura da
coluna ser de 14 mdulos, implica ainda que a altura do fuste ser de
13 vezes o raio.
Seja a altura do capitel dividida em trs partes, das quais uma formar o
baco com o seu cimteo, o segundo o quino (coxim) com os seus aneis e o
terceiro o pescoo.
Isto quer dizer que o baco tem uma altura de um tero de um modulo, ou seja 13 do raio da base, e o coxim ter os restantes dois teros,
ou seja, 32 do raio da base.
Estas consideraes levam-nos a poder determinar o valor de alguns
dos parmetros de desenho das colunas dricas em termos do raio da base
do fuste. Em termos de implementao, isso quer dizer que os parmetros
da funo passam a ser variveis locais cuja atribuio feita aplicando
as propores estabelecidas por Vitrvio ao parmetro r-base-fuste. A
definio da funo fica ento:
(defun coluna (p
r-base-fuste r-base-coxim

117

Figura 26: Variaes de colunas dricas segundo as propores de Vitrvio.


/ a-fuste a-coxim a-abaco l-abaco)
(setq a-fuste (* 13 r-base-fuste)
a-coxim (* (/ 2.0 3) r-base-fuste)
a-abaco (* (/ 1.0 3) r-base-fuste)
l-abaco (* (/ 13.0 6) r-base-fuste))
(fuste p a-fuste r-base-fuste r-base-coxim)
(coxim (+z p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
(abaco (+z p (+ a-fuste a-coxim)) a-abaco l-abaco))

Usando esta funo j possvel desenhar colunas que se aproximam


mais do padro drico (tal como estabelecido por Vitrvio).41 A Figura 26
apresenta as colunas desenhadas pelas seguintes invocaes:
(coluna
(coluna
(coluna
(coluna
(coluna
(coluna

(xyz 0 0
(xyz 3 0
(xyz 6 0
(xyz 9 0
(xyz 12 0
(xyz 15 0

0)
0)
0)
0)
0)
0)

0.3
0.5
0.4
0.5
0.5
0.4

0.2)
0.3)
0.2)
0.4)
0.5)
0.7)

41

A Ordem Drica estabelece ainda mais uma relao entre os parmetros das colunas,
relao essa que s iremos implementar na seco 7.10.

118

7.8

Seleco

As propores de Vitrvio permitiram-nos reduzir o nmero de parmetros independentes de uma coluna Drica a apenas dois: o raio da base
do fuste e o raio da base do coxim. Contudo, no parece correcto que estes parmetros sejam totalmente independentes pois isso permite construir
colunas aberrantes em que o topo do fuste mais largo do que a base, tal
como acontece com a coluna mais direita na Figura 26.
Na verdade, a caracterizao da Ordem Drica que apresentmos encontrase incompleta pois, acerca das propores das colunas, Vitrvio afirmou
ainda que:
. . . se uma coluna tem quinze ps ou menos, divida-se a
largura na base em seis partes e usem-se cinco dessas partes
para formar a largura no topo, . . . [Vitrvio, Os Dez Livros da
Arquitectura, Livro III, Cap. 3.1]
At agora, fomos capazes de traduzir todas as regras que fomos anunciando mas, desta vez, a traduo levanta-nos uma nova dificuldade: ser
possvel traduzir uma afirmao da forma se, ento para Auto Lisp?
O termo se, ento permite-nos executar uma aco dependendo de
uma determinada condio ser verdadeira ou falsa: se uma coluna tem
quinze ps ou menos, ento o topo da coluna tem 56 da base, caso contrrio,
. . . . Na linguagem Lisp, estes termos descrevem uma forma de actuao
que se denomina de estrutura de controle de seleco. A seleco permitenos escolher uma via de aco mas apenas se uma determinada condio
for verdadeira.
A estrutura de controle de seleco mais sofisticada que a sequenciao. Ela baseia-se na utilizao de expresses condicionais para decidir
qual a prxima expresso a avaliar. A forma mais simples desta estrutura
de controle o if cuja sintaxe a seguinte:
(if condicional
consequente
alternativa)

O valor de uma combinao cujo primeiro elemento o if obtido da


seguinte forma:
1. A expresso condicional avaliada.
2. Se o valor obtido da avaliao anterior verdade, o valor da combinao o valor da expresso consequente.
119

3. Caso contrrio, o valor obtido da avaliao anterior falso e o valor


da combinao o valor da expresso alternativa.
Este comportamento pode ser confirmado pelos seguintes exemplos:
_$ (if (> 3 2)
1
2)
1
_$ (if (> 3 4)
1
2)
2

Usando o if podemos definir funes cujo comportamento depende


de uma ou mais condies. Por exemplo, consideremos a funo max que
recebe dois nmeros como argumentos e devolve o maior deles. Para definirmos esta funo apenas precisamos de testar se o primeiro argumento
maior que o segundo. Se for, a funo devolve o primeiro argumento,
caso contrrio devolve o segundo. Com base neste raciocnio, podemos
escrever:
(defun max (x y)
(if (> x y)
x
y))

Muitas outras funes podem ser definidas custa do if. Por exemplo,
para calcularmos o valor absoluto de um nmero x, |x|, temos de saber se
ele negativo. Se for, o seu valor absoluto o seu simtrico, caso contrrio
ele prprio. Eis a definio:
(defun abs (x)
(if (< x 0)
(- x)
x))

Um outro exemplo mais interessante ocorre com a funo matemtica


sinal sgn, tambm conhecida como funo signum (sinal, em Latim). Esta
funo pode ser vista como a funo dual da funo valor absoluto pois
tem-se sempre x = sgn(x)|x|. A funo sinal definida por

1 se x < 0
sgn x = 0
se x = 0

1
caso contrrio
120

Quando definimos esta funo em Lisp notamos que necessrio empregar mais do que um if:
(defun signum (x)
(if (< x 0)
-1
(if (= x 0)
0
1)))

7.9

Seleco MltiplaA Forma cond

Quando uma definio de funo necessita de vrios ifs encadeados,


normal que o cdigo comece a ficar mais difcil de ler. Neste caso, prefervel usar uma outra forma que torna a definio da funo mais legvel.
Para evitar muitos ifs encadeados o Lisp providencia uma outra forma
denominada cond cuja sintaxe a seguinte:
(cond (expr0,0 expr0,1 ... expr0,n )
(expr1,0 expr1,1 ... expr1,m )
...
(exprk,0 exprk,1 ... exprk,p ))

O cond aceita qualquer nmero de argumentos. Cada argumento denominado clusula e constitudo por uma lista de expresses. A semntica
do cond consiste em avaliar sequencialmente a primeira expresso expri,0
de cada clusula at encontrar uma cujo valor seja verdade. Nesse momento, o cond avalia todas as restantes expresses dessa clusula e devolve
o valor da ltima. Se nenhuma das clusulas tiver uma primeira expresso
que avalie para verdade, o cond devolve nil. Se a clusula cuja primeira
expresso verdade no contiver mais expresses, o cond devolve o valor
dessa primeira expresso.
importante perceber que os parntesis que envolvem as clusulas no
correspondem a nenhuma combinao: eles simplesmente fazem parte da
sintaxe do cond e so necessrios para separar as clusulas umas das outras.
A pragmtica usual para a escrita de um cond (em especial quando
cada clusula contm apenas duas expresses) consiste em alinhar as expresses umas debaixo das outras.
Usando o cond, a funo sinal pode ser escrita de forma mais simples:
(defun signum (x)
(cond ((< x 0)

121

-1)
((= x 0)
0)
(t
1)))

Note-se, no exemplo anterior, que a ltima clusula do cond tem, como


expresso lgica, o smbolo t. Como j vimos, este smbolo representa a
verdade pelo que a sua presena garante que ela ser avaliada no caso de
nenhuma das clusulas anteriores o ter sido. Neste sentido, clusula da
forma (t ...) representa um em ltimo caso . . . .
Exerccio 7.9.1 Qual o significado de (cond (expr1 expr2))?
Exerccio 7.9.2 Qual o significado de (cond (expr1) (expr2))?

A forma cond no estritamente necessria na linguagem pois possvel fazer exactamente o mesmo com uma combinao entre formas if
encadeadas e formas progn, tal como a seguinte equivalncia o demonstra:
(cond (expr0,0 expr0,1 ... expr0,n )
(expr1,0 expr1,1 ... expr1,m )
.
.
.
(exprk,0 exprk,1 ... exprk,p ))

m
(if expr0,0
(progn
expr0,1 ... expr0,n )
(if expr1,0
(progn
expr1,1 ... expr1,m )
..
.
(if exprk,0
(progn
exprk,1 ... exprk,p )
nil)))

Contudo, pragmaticamente falando, a forma cond pode simplificar substancialmente o programa quando o nmero de if encadeados maior que
um ou dois ou quando se pretende usar sequenciao no consequente ou
alternativa do if.
122

Exerccio 7.9.3 Defina uma funo soma-maiores que recebe trs nmeros como argumento e determina a soma dos dois maiores.
Exerccio 7.9.4 Defina a funo max3 que recebe trs nmeros como argumento e calcula o
maior entre eles.
Exerccio 7.9.5 Defina a funo segundo-maior que recebe trs nmeros como argumento
e devolve o segundo maior nmero, i.e., que est entre o maior e o menor.

7.10

Seleco nas Propores de Vitrvio

Uma vez compreendida a estrutura de controle de seleco, estamos em


condies de implementar a ltima regra de Vitrvio sobre as propores
das colunas da Ordem Drica. A regra afirma que:
A diminuio no topo de uma coluna parece ser regulada
segundo os seguintes princpios: se uma coluna tem menos de
quinze ps, divida-se a largura na base em seis partes e usem-se
cinco dessas partes para formar a largura no topo. Se a coluna
tem entre quinze e vinte ps, divida-se a largura na base em
seis partes e meio e usem-se cinco e meio dessas partes para a
largura no topo da coluna. Se a coluna tem entre vinte e trinta
ps, divida-se a largura na base em sete partes e faa-se o topo
diminuido medir seis delas. Uma coluna de trinta a quarenta
ps deve ser dividida na base em sete partes e meia e, no princpio da diminuio, deve ter seis partes e meia no topo. Colunas de quarenta a cinquenta ps devem ser divididas em oito
partes e diminuidas para sete delas no topo da coluna debaixo
do capitel. No caso de colunas mais altas, determine-se proporcionalmente a diminuio com base nos mesmos princpios.
[Vitrvio, Os Dez Livros da Arquitectura, Livro III, Cap. 3.1]
Estas consideraes de Vitrvio permitem-nos determinar a razo entre
o topo e a base de uma coluna em funo da sua altura em ps.42
Consideremos ento a definio de uma funo, que iremos denominar
de raio-topo-fuste, que recebe como parmetros a largura da base da
42

O p foi a unidade fundamental de medida durante inmeros sculos mas a sua real
dimenso variou ao longo do tempo. O comprimento do p internacional de 304.8 milmetros e foi estabelecido por acordo em 1958. Antes disso, vrios outros comprimentos
foram usados, como o p Drico de 324 milmetros, os ps Jnico e Romano de 296 milmetros, o p Ateniense de 315 milmetros, os ps Egpcio e Fencio de 300 milmetros, etc.

123

coluna e a altura da coluna e devolve como resultado a largura do topo da


coluna.
Uma traduo literal das consideraes de Vitrvio para Lisp permitenos comear por escrever:
(defun raio-topo-fuste (raio-base altura)
(cond ((< altura 15) (* (/ 5.0 6.0) raio-base))
...))

O fragmento anterior corresponde, obviamente, afirmao: se uma coluna tem menos de quinze ps, divida-se a largura na base em seis partes e usem-se
cinco dessas partes para formar a largura no topo. No caso de a coluna no ter
menos de quinze ps, ento passamos ao caso seguinte: se a coluna tem entre
quinze e vinte ps, divida-se a largura na base em seis partes e meio e usem-se cinco
e meio dessas partes para a largura no topo da coluna. A traduo deste segundo
caso permite-nos escrever:
(defun raio-topo-fuste (raio-base altura)
(cond ((< altura 15)
(* (/ 5.0 6.0) raio-base))
((and (>= altura 15) (< altura 20))
(* (/ 5.5 6.5) raio-base))
...))

Uma anlise cuidadosa das duas clusulas anteriores mostra que, na


realidade, estamos a fazer testes a mais na segunda clusula. De facto, se
conseguimos chegar segunda clusula porque a primeira falsa, i.e., a
altura no menor que 15 e, portanto, maior ou igual a 15. Nesse caso,
intil estar a testar novamente se a altura maior ou igual a 15. Assim,
podemos simplificar a funo e escrever:
(defun raio-topo-fuste (raio-base altura)
(cond ((< altura 15) (* (/ 5.0 6.0) raio-base))
((< altura 20) (* (/ 5.5 6.5) raio-base))
...))

A continuao da traduo levar-nos-, ento, a:


(defun raio-topo-fuste
(cond ((< altura 15)
((< altura 20)
((< altura 30)
((< altura 40)
((< altura 50)
...))

(raio-base altura)
(* (/ 5.0 6.0) raio-base))
(* (/ 5.5 6.5) raio-base))
(* (/ 6.0 7.0) raio-base))
(* (/ 6.5 7.5) raio-base))
(* (/ 7.0 8.0) raio-base))

124

O problema agora que Vitrvio deixou a porta aberta para colunas


arbitrariamente altas, dizendo simplesmente que no caso de colunas mais altas, determine-se proporcionalmente a diminuio com base nos mesmos princpios.
Para percebermos claramente de que princpios estamos a falar, consideremos a evoluo da relao entre o topo e a base das colunas que visvel
na imagem lateral.
A razo entre o raio do topo da coluna e o raio da base da coluna
, tal como se pode ver na margem (e j era possvel deduzir da funo
raio-topo-fuste), uma sucesso da forma
5 5 12 6 6 12 7
, ,
, ,
,
6 6 12 7 7 12 8
Torna-se agora bvio que, para colunas mais altas, os mesmos princpios de que Vitrvio fala se resumem a, para cada 10 ps adicionais, somar
1
2 quer ao numerador, quer ao denominador. No entanto, importante reparar que este princpio s pode ser aplicado a partir dos 15 ps de altura
pois o primeiro intervalo maior que os restantes. Assim, temos de tratar de forma diferente as colunas at aos 15 ps e, da para a frente, basta
subtrair 20 ps altura e determinar a diviso inteira por 10 para saber o
nmero de vezes que precisamos de somar 21 quer ao numerador quer ao
denominador de 67 .
este tratar de forma diferente um caso e outro que, mais uma vez,
sugere a necessidade de utilizao de uma estrutura de controle de seleco: necessrio distinguir dois casos e reagir em conformidade para cada
um. No caso da coluna de Vitrvio, se a coluna tem uma altura a at 15
ps, a razo entre o topo e a base r = 56 ; se a altura a no inferior a 15
ps, a razo r entre o topo e a base ser:
6 + b a20
10 c
r=
a20
7 + b 10 c

1
2
1
2

A ttulo de exemplo, consideremos uma coluna com 43 ps de altura. A


diviso inteira de 43 20 por 10 2 portanto temos de somar 2 12 = 1 ao
numerador e denominador de 76 , obtendo 78 = 0.875.
Para um segundo exemplo nos limites do absurdo, consideremos uma
coluna da altura do Empire State Building, i.e., com 449 metros de altura.
Um p, na ordem Drica, media 324 milmetros pelo que em 449 metros
existem 449/0.324 1386 ps. A diviso inteira de 1386 20 por 10
136. A razo entre o topo e a base desta hipottica coluna ser ento de
125

50
7
8

40
6 12
7 12

30
6
7

20
15

5 12
6 12

5
6

6+136/2
7+136/2

= 74
75 = 0.987. Este valor, por ser muito prximo da unidade, mostra
que a coluna seria praticamente cilndrica.
Com base nestas consideraes, podemos agora definir uma funo
que, dado um nmero inteiro representando a altura da coluna em ps,
calcula a razo entre o topo e a base da coluna. Antes, contudo, convm
simplificar a frmula para as colunas com altura no inferior a 15 ps. Assim,
r=

6 + b a20
10 c
a20
7 + b 10 c

1
2
1
2

a
a
12 + b a20
12 + b 10
c2
c
10 + b 10
10 c
=
=
a
a
a20
14 + b 10 c 2
12 + b 10 c
14 + b 10 c

A definio da funo fica ento:


(defun raio-topo-fuste (raio-base altura / divisoes)
(setq divisoes (fix (/ altura 10)))
(if (< altura 15)
(* (/ 5.0 6.0)
raio-base)
(* (/ (+ 10.0 divisoes)
(+ 12.0 divisoes))
raio-base)))

Esta a ltima relao que nos falta para especificarmos completamente


o desenho de uma coluna drica de acordo com as propores referidas
por Vitrvio no seu tratado de arquitectura. Vamos considerar, para este
desenho, que vamos fornecer as coordenadas do centro da base da coluna
e a sua altura. Todos os restantes parmetros sero calculados em termos
destes. Eis a definio da funo:
(defun coluna-dorica (p altura /
a-fuste r-base-fuste
a-coxim r-base-coxim
a-abaco l-abaco)
(setq r-base-fuste (/ altura 14.0)
r-base-coxim (raio-topo-fuste r-base-fuste altura)
a-fuste (* 13.0 r-base-fuste)
a-coxim (* (/ 2.0 3) r-base-fuste)
a-abaco (* (/ 1.0 3) r-base-fuste)
l-abaco (* (/ 13.0 6) r-base-fuste))
(fuste p a-fuste r-base-fuste r-base-coxim)
(coxim (+z p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0))
(abaco (+z p (+ a-fuste a-coxim)) a-abaco l-abaco))

O seguinte exemplo de utilizao da funo produz a sequncia de colunas apresentadas na Figura 27:43
43

Note-se que, agora, a altura da coluna tem de ser especificada em ps dricos.

126

Figura 27: Variaes de colunas dricas segundo as propores de Vitrvio.


(coluna-dorica (xy 0 0) 10)
(coluna-dorica (xy 10 0) 15)
(coluna-dorica (xy 20 0) 20)
(coluna-dorica (xy 30 0) 25)
(coluna-dorica (xy 40 0) 30)
(coluna-dorica (xy 50 0) 35)

Exerccio 7.10.1 A funo raio-topo-fuste calcula o valor da varivel local divisoes


mesmo quando o parmetro altura menor ou igual a 15. Redefina a funo de modo a
que a varivel divisoes s seja definida valor quando o seu valor realmente necessrio.

7.11

Operaes com Coordenadas

Vimos, na seco 4.1, a definio e implementao de um tipo abstracto


para coordenadas bi-dimensionais que assentava nas seguinte funes:
127

(defun xy (x y)
(list x y))
(defun cx (c)
(car c))
(defun cy (c)
(cadr c))

Vimos tambm, na seco 4.6 que possvel definir as coordenadas tridimensionais como uma generalizao bvia do tipo anterior. Para isso,
bastou-nos preservar as posies das coordenadas bi-dimensionais x e y na
representao de coordenadas tri-dimensionais, o que implica colocarmos
a coordenada z a seguir s outras duas. Isto levou-nos a definir o construtor
de coordenadas Cartesianas tri-dimensionais xyz com a seguinte forma:
(defun xyz (x y z)
(list x y z))

O correspondente selector da coordenada z ficou ento definido como:


(defun cz (c)
(caddr c))

Uma vez que adoptmos uma representao de coordenadas tri-dimensionais


que uma extenso da representao correspondente para coordenadas
bi-dimensionais, os selectores cx e cy que eram vlidos para coordenadas bi-dimensionais passaram a ser vlidos tambm para coordenadas tridimensionais. Infelizmente, o inverso j no necessariamente verdade:
o selector cz no pode ser aplicado a coordenadas bi-dimensionais pois
estas no possuem, na sua representao, esta terceira coordenada. No entanto, possvel generalizar este selector de modo a torn-lo aplicvel tambm a coordenadas bi-dimensionais, simplesmente assumindo que o caso
bi-dimensional se desenrola no plano z = 0, i.e., assumindo que (x, y) =
(x, y, 0). Em termos de implementao, para que o selector cz seja compatvel com coordenadas bi- ou tri-dimensionais, ele precisa de saber distinguir
as duas representaes. Uma vez que uma das representaes uma lista
de dois elementos e a outra uma lista de trs elementos, o mais simples
testar se existe algo para l do segundo elemento, i.e., se o resto do resto da
lista no est vazia. Como vimos na seco 6.10, a funo null reconhece a
lista vazia, o que nos permite definir um reconhecedor diferente para cada
tipo de coordenada:

128

(defun xy? (c)


(null (cddr c)))
(defun xyz? (c)
(not (null (cddr c))))

Usando estas funes agora mais simples definir correctamente o selector cz: para coordenadas bi-dimensionais, devolve zero. Para coordenadas tri-dimensionais, devolve o terceiro elemento da lista de coordenadas.
(defun cz (c)
(if (xy? c)
0
(caddr c)))

de salientar que o selector cz precisa de testar o seu argumento para


decidir o que devolver. Este teste e escolha do caminho a seguir a marca
da estrutura de controle de seleco.
A redefinio do selector cz para lidar com os dois tipos de coordenadas tem a vantagem adicional de permitir que outras funes de coordenadas tri-dimensionais se possam aplicar a coordenadas bi-dimensionais.
Por exemplo, a seguinte funo de coordenadas tri-dimensionais, definida
na seco 4.5,
(defun +xyz (p dx dy dz)
(xyz (+ (cx p) dx)
(+ (cy p) dy)
(+ (cz p) dz)))

passa a ser directamente aplicvel a coordenadas bi-dimensionais sem qualquer tipo de alterao.
Infelizmente, nem todas as funes conseguem funcionar inalteradas.
Por exemplo, consideremos a funo +xy que permitia deslocar coordenadas bi-dimensionais:
(defun +xy (c x y)
(xy (+ (cx c) x)
(+ (cy c) y)))

Como bvio pela anlise da funo anterior, se ela for aplicada a uma
coordenada tri-dimensional, perde-se a coordenada z, o que manifestamente indesejvel. Para evitar este problema e, ao mesmo tempo, preservar
a compatibilidade com o caso bi-dimensional, temos de distinguir os dois
casos:
129

(defun +xy (c x y)
(if (xy? c)
(xy (+ (cx c) x)
(+ (cy c) y))
(xyz (+ (cx c) x)
(+ (cy c) y)
(cz c))))

Como exemplo, temos:


_$
(5
_$
(5

(+xy (xy 1 2) 4 5)
7)
(+xy (xyz 1 2 3) 4 5)
7 3)

Para alm da funo +xy tambm usmos anteriormente a funo +pol


que somava a um ponto uma distncia especificada em termos de coordenadas polares:
(defun +pol (c ro fi)
(+xy c
(* ro (cos fi))
(* ro (sin fi))))

Uma vez que a funo +pol est definida em termos da funo +xy
e esta j sabe tratar coordenadas tri-dimensionais, tambm +pol saber
tratar coordenadas tri-dimensionais e, consequentemente, no precisa de
ser redefinida. O mesmo se pode dizer das funes +x e +y que apenas
dependem de +xy.
Finalmente, consideremos a definio da funo +c que apresentmos
na seco 4.11 e que se destinava a deslocar um ponto ao longo de um
vector (especificado pelas coordenadas da sua extremidade):
(defun +c (p0 p1)
(xy (+ (cx p0) (cx p1))
(+ (cy p0) (cy p1))))

evidente pela definio da funo que ela no aplicvel ao caso


tri-dimensional pois s lida com as coordenadas x e y de ambos os pontos. Para lidar correctamente com o caso tri-dimensional teremos de ter em
conta que qualquer um dos parmetros pode corresponder a um ponto em
coordenadas tri-dimensionais e que ser apenas quando ambos os parmetros forem coordenadas bi-dimensionais que poderemos usar a definio
anterior. Assim, temos:
130

(defun +c (p0 p1)


(if (and (xy? p0) (xy? p1))
(xy (+ (cx p0) (cx p1))
(+ (cy p0) (cy p1)))
(xyz (+ (cx p0) (cx p1))
(+ (cy p0) (cy p1))
(+ (cz p0) (cz p1)))))

Agora, temos:
_$
(4
_$
(4
_$
(3
_$
(3

(+c (xy 1 2)
6)
(+c (xy 1 2)
6 5)
(+c (xyz 0 1
5 2)
(+c (xyz 0 1
5 7)

(xy 3 4))
(xyz 3 4 5))
2) (xy 3 4))
2) (xyz 3 4 5))

Para alm da soma de um vector a um ponto por vezes til considerar a operao inversa, que subtrai um vector a um ponto, i.e., que
determina a origem do vector cujo destino o ponto dado ou, equivalentemente, as projeces do vector que liga os dois pontos dados. Por analogia
com a operao +c, vamos denominar esta operao de -c e a sua definio
ser, obviamente:
(defun -c (p0 p1)
(if (and (xy? p0) (xy? p1))
(xy (- (cx p0) (cx p1))
(- (cy p0) (cy p1)))
(xyz (- (cx p0) (cx p1))
(- (cy p0) (cy p1))
(- (cz p0) (cz p1)))))

Agora, temos:
_$
(1
_$
(3
_$
(3
_$
(3
_$
(4

(-c (xy 4 6)
2)
(-c (xy 4 6)
4))
(-c (xyz 4 6
4 0)
(-c (xyz 4 6
4 5)
(-c (xyz 5 7
5 6)

(xy 3 4))
(xy 1 2))
3) (xyz 1 2 3))
5) (xy 1 2))
9) (xyz 1 2 3))

131

Pode ser tambm til poder multiplicar uma coordenada por um factor, correspondendo a uma simples homotetia. Mais uma vez, temos de
distinguir os dois casos de pontos bi- e tri-dimensionais:
(defun *c (p f)
(if (xy? p)
(xy (* (cx p) f)
(* (cy p) f))
(xyz (* (cx p) f)
(* (cy p) f)
(* (cz p) f))))

Como se pode ver pelas definies anteriores, a estrutura de controle de


seleco fundamental para se poder obter os diferentes comportamentos
necessrios para tratar o caso de coordenadas Cartesianas bi-dimensionais
ou tri-dimensionais.
Finalmente, para alm das operaes de adio e subtraco de coordenadas, pode ser til uma operao de comparao de coordenadas que
designaremos por =c. Duas coordenadas sero iguais se as suas componentes x, y e z forem iguais, i.e.:
(defun =c (c0
(and (= (cx
(= (cy
(= (cz

c1)
c0) (cx c1))
c0) (cy c1))
c0) (cz c1))))

A funo anterior funciona igualmente bem com coordenadas bi-dimensionais,


tri-dimensionais ou mistas pois elas s podero diferir na presena, ou no,
da coordenada z mas o selector cz encarrega-se de esconder essa diferena.
O conjunto de funes que descrevemos atrs e que inclui os construtores xy, +xy, xyz, +xyz, +x, +y, +z, +c, -c e *c, os selectores cx, cy e cz,
os reconhecedores xy? e xyz? e, finalmente, o teste =c constituem um tipo
abstracto para coordenadas Cartesianas que iremos frequentemente usar
de futuro.
Para alm das coordenadas Cartesianas, ainda usual empregarem-se
dois outros sistemas de coordenadas que iremos ver de seguida: as coordenadas cilndricas e as coordenadas esfricas.

7.12

Coordenadas Cilndricas

Tal como podemos verificar na Figura 28, um ponto, em coordenadas cilndricas, caracteriza-se por um raio assente no plano z = 0, um ngulo
que esse raio faz com o eixo x e por uma cota z. fcil de ver que o raio e
132

z
y

x
Figura 28: Coordenadas cilndricas.
o ngulo correspondem s coordenadas polares da projeco do ponto no
plano z = 0.
Dado um ponto (, , z) em coordenadas cilndricas, o mesmo ponto
em coordenadas Cartesianas
( cos , sin , z)
De igual modo, dado um ponto (x, y, z) em coordenadas Cartesianas, o
mesmo ponto em coordenadas cilndricas
(

p
y
x2 + y 2 , atan , z)
x

Estas equivalncias permitem-nos definir uma funo que constri pontos em coordenadas cilndricas empregando as coordenadas Cartesianas
como representao cannica:
(defun cil (ro fi z)
(xyz (* ro (cos fi))
(* ro (sin fi))
z))

No caso de pretendermos simplesmente somar a um ponto um vector


em coordenadas cilndricas, podemos tirar partido da funo +c e definir:

133

(defun +cil (p ro fi z)
(+c p (cil ro fi z)))
Exerccio 7.12.1 Defina os selectores cil-ro, cil-fi e cil-z que devolvem, respectivamente, os componentes , e z de um ponto construdo pelo construtor cil.

Existem inmeras curvas, superfcies e volumes que so mais simplesmente descritos usando coordenadas cilndricas do que usando coordenadas rectangulares. Por exemplo, a equao paramtrica de uma hlice, em
coordenadas cilndricas, trivial:
(, , z) = (1, t, t)
No entanto, em coordenadas rectangulares, somos obrigados a empregar
alguma trigonometria para obter a equao equivalente:
(x, y, z) = (cos t, sin t, t)
Exerccio 7.12.2 Defina uma funo escadas capaz de construir uma escada em hlice. A
imagem seguinte mostra trs exemplos destas escadas.

Como se pode ver pela imagem anterior, a escada constituda por um cilindro central
no qual se apoiam 10 degraus cilndricos. Para facilitar a experimentao considere que o
cilindro central de raio r. Os degraus so iguais ao cilindro central, possuem 10 raios de
comprimento e esto dispostos a alturas progressivamente crescentes. Cada degrau est
a uma altura h em relao ao degrau anterior e, visto em planta, faz um ngulo com o
degrau anterior.
A funo escada dever receber as coordenadas do centro da base do cilindro central,
o raio r, a altura h e o ngulo . A ttulo de exemplo, considere que as trs escadas anteriores
foram construdas pelas seguintes invocaes:
(escada (xyz 0 0 0) 1.0 3 (/ pi 6))
(escada (xyz 0 40 0) 1.5 5 (/ pi 9))
(escada (xyz 0 80 0) 0.5 6 (/ pi 8))

134

P

y

Figura 29: Coordenadas Esfricas

7.13

Coordenadas Esfricas

Tal como podemos ver na Figura 29, um ponto, em coordenadas esfricas


(tambm denominadas polares), caracteriza-se pelo comprimento do raio
vector, por um ngulo (denominado longitude ou azimute) que a projeco
desse vector no plano z = 0 faz com o eixo x e por ngulo (denominado
colatitude44 , znite ou ngulo polar) que o vector faz com o eixo z.
Dado um ponto (, , ) em coordenadas esfricas, o mesmo ponto em
coordenadas Cartesianas
( sin cos , sin sin , cos )
De igual modo, dado um ponto (x, y, z) em coordenadas Cartesianas, o
mesmo ponto em coordenadas esfricas
p
p
y
x2 + y 2
( x2 + y 2 + z 2 , atan , atan
)
x
z
Tal como fizemos para as coordenadas cilndricas, vamos definir o construtor de coordenadas esfricas e a soma de um ponto a um vector em coordenadas esfricas:
44

A colatitude , como bvio, o ngulo complementar latitude, i.e., a diferena entre


o plo ( 2 ) e a latitude.

135

(defun esf (ro fi


(xyz (* ro (sin
(* ro (sin
(* ro (cos

psi)
psi) (cos fi))
psi) (sin fi))
psi))))

(defun +esf (p ro fi psi)


(+c p (esf ro fi psi)))
Exerccio 7.13.1 Defina os selectores esf-ro, esf-fi e esf-psi que devolvem, respectivamente, os componentes , e de um ponto construdo pelo construtor esf.

7.14

Recurso

Vimos que as nossas funes, para fazerem algo til, precisam de invocar
outras funes. Por exemplo, se j tivermos a funo que calcula o quadrado de um nmero e pretendermos definir a funo que calcula o cubo
de um nmero, podemos facilmente faz-lo custa do quadrado e de uma
multiplicao adicional, i.e.:
(defun cubo (x)
(* (quadrado x) x))

Do mesmo modo, podemos definir a funo quarta-potencia custa


do cubo e de uma multiplicao adicional, i.e.:
(defun quarta-potencia (x)
(* (cubo x) x))

Como bvio, podemos continuar a definir sucessivamente novas funes para calcular potncias crescentes mas isso no s moroso como ser
sempre limitado. Seria muito mais til podermos generalizar o processo
e definir simplesmente a funo potncia que, a partir de dois nmeros (a
base e o expoente) calcula o primeiro elevado ao segundo.
No entanto, aquilo que fizemos para a quarta-potencia, o cubo e
o quadrado do-nos uma pista muito importante: se tivermos uma funo
que calcula a potncia de expoente imediatamente inferior, ento basta-nos uma
multiplicao adicional para calcular a potncia seguinte.
Dito de outra forma, temos:
(defun potencia (x n)
(* (potencia-inferior x n) x))

Embora tenhamos conseguido simplificar o problema do clculo de potncia, sobrou uma questo por responder: como podemos calcular a potncia imediatamente inferior? A resposta poder no ser bvia mas, uma
136

vez percebida, trivial: a potncia imediatamente inferior potncia de expoente


n a potncia de expoente (n1). Isto implica que (potencia-inferior x n)
exactamente o mesmo que (potencia x (- n 1)). Com base nesta
ideia, podemos reescrever a definio anterior:
(defun potencia (x n)
(* (potencia x (- n 1)) x))

Apesar da nossa soluo engenhosa, esta definio tem um problema:


qualquer que seja a potncia que tentemos calcular, nunca conseguiremos
obter o resultado final. Para percebermos este problema, mais simples
usar um caso real: tentemos calcular a terceira potncia do nmero 4, i.e.,
(potencia 4 3).
Para isso, de acordo com a definio da funo potencia, ser preciso
avaliar a expresso
(* (potencia 4 2) 4)
que, por sua vez, implica avaliar
(* (* (potencia 4 1) 4) 4)
que, por sua vez, implica avaliar
(* (* (* (potencia 4 0) 4) 4) 4)
que, por sua vez, implica avaliar
(* (* (* (* (potencia 4 -1) 4) 4) 4) 4)
que, por sua vez, implica avaliar
(* (* (* (* (* (potencia 4 -2) 4) 4) 4) 4) 4)
que, por sua vez, implica avaliar
(* (* (* (* (* (* (potencia 4 -3) 4) 4) 4) 4) 4) 4)
que, por sua vez, implica avaliar . . .
fcil vermos que este processo nunca mais termina. O problema est
no facto de termos reduzido o clculo da potncia de um nmero elevado
a um expoente ao clculo da potncia desse nmero elevado ao expoente
imediatamente inferior mas no dissemos em que situao que j temos
um expoente suficientemente simples cuja soluo seja imediata. Quais so
as situaes em que isso acontece? J vimos que quando o expoente 2, a
funo quadrado devolve o resultado correcto, pelo que o caso n = 2
j suficientemente simples. No entanto, possvel ter um caso ainda mais
simples: quando o expoente 1, o resultado simplesmente a base. Finalmente, o caso mais simples de todos: quando o expoente zero, o resultado 1, independentemente da base. Este ltimo caso fcil de perceber quando vemos que a avaliao de (potencia 4 2) (i.e., do quadrado
de quatro) se reduz, em ltima anlise, a (* (* (potencia 4 0) 4) 4).
137

Para que esta expresso seja equivalente a (* 4 4) necessrio que (potencia 4 0)


seja 1.
Estamos ento em condies de definir correctamente a funo potencia:
1. Quando o expoente zero, o resultado um.
2. Caso contrrio, calculamos a potncia de expoente imediatamente inferior e multiplicamo-la pela base.
(defun potencia (x n)
(if (zerop n)
1
(* (potencia x (- n 1)) x)))

A funo anterior um exemplo de uma funo recursiva, i.e., uma funo que est definida em termos de si prpria. Dito de outra forma, uma
funo recursiva uma funo que se usa a si prpria na sua definio. Esse
uso bvio quando desenrolamos a avaliao de (potencia 4 3):

(*
(* (*

(potencia 4 3)

(* (potencia 4 2) 4)

(* (potencia 4 1) 4) 4)

(* (potencia 4 0) 4) 4) 4)

(* (* (* 1 4) 4) 4)

(* (* 4 4) 4)

(* 16 4)

64

A recurso a estrutura de controle que permite que uma funo se


possa invocar a si prpria durante a sua prpria avaliao. A recurso
uma das mais importantes ferramentas de programao pelo que fundamental que a percebamos bem. Muitos problemas aparentemente complexos possuem solues recursivas extraordinariamente simples.
Existem inmeros exemplos de funes recursivas. Uma das mais simples a funo factorial que se define matematicamente como:
138

(
1,
se n = 0
n! =
n (n 1)!, caso contrrio.
A traduo desta frmula para Lisp directa:
(defun factorial (n)
(if (zerop n)
1
(* n (factorial (- n 1)))))

importante repararmos que em todas as funes recursivas existe:


Um caso bsico (tambm chamado caso de paragem) cujo resultado
imediatamente conhecido.
Um caso no bsico (tambm chamado caso recursivo) em que se transforma o problema original num sub-problema mais simples.
Se analisarmos a funo factorial, o caso bsico o teste de igualdade a
zero (zerop n), o resultado imediato 1, e o caso recursivo , obviamente,
(* n (factorial (- n 1))).
Geralmente, uma funo recursiva s est correcta se tiver uma expresso condicional que identifique o caso bsico, mas no obrigatrio que
assim seja. A invocao de uma funo recursiva consiste em ir resolvendo
subproblemas sucessivamente mais simples at se atingir o caso mais simples de todos, cujo resultado imediato. Desta forma, o padro mais comum para escrever uma funo recursiva :
Comear por testar os casos bsicos.
Fazer uma invocao recursiva com um subproblema cada vez mais
prximo de um caso bsico.
Usar o resultado da invocao recursiva para produzir o resultado da
invocao original.
Dado este padro, os erros mais comuns associados s funes recursivas so, naturalmente:
No detectar um caso bsico.
A recurso no diminuir a complexidade do problema, i.e., no passar para um problema mais simples.
139

No usar correctamente o resultado da recurso para produzir o resultado originalmente pretendido.


Repare-se que uma funo recursiva que funciona perfeitamente para
os casos para que foi prevista pode estar completamente errada para outros
casos. A funo factorial um exemplo: quando o argumento negativo, o problema torna-se cada vez mais complexo, cada vez mais longe do
caso simples:
(factorial -1)

(* -1 (factorial -2))

(* -1 (* -2 (factorial -3)))

(* -1 (* -2 (* -3 (factorial -4))))

(* -1 (* -2 (* -3 (* -4 (factorial -5))))))

(* -1 (* -2 (* -3 (* -4 (* -5 (factorial -6))))))

...
O caso mais frequente de erro numa funo recursiva a recurso nunca
parar, ou porque no se detecta correctamente o caso bsico ou por a recurso no diminuir a complexidade do problema. Neste caso, o nmero
de invocaes recursivas cresce indefinidamente at esgotar a memria do
computador, altura em que o programa gera um erro. No caso do Auto
Lisp, esse erro no inteiramente bvio pois o avaliador apenas interrompe
a avaliao sem apresentar qualquer resultado. Eis um exemplo:
_$ (factorial 3)
6
_$ (factorial -1)
_$

muito importante compreendermos bem o conceito de recurso. Embora a princpio possa ser difcil abarcar por completo as implicaes deste
conceito, a recurso permite resolver, com enorme simplicidade, problemas
aparentemente muito complexos.
Como iremos ver, tambm em arquitectura a recurso um conceito
fundamental.
140

Exerccio 7.14.1 A funo de Ackermann definida para nmeros no negativos da seguinte forma:
8
>
se m = 0
<n + 1
A(m, n) = A(m 1, 1)
se m > 0 e n = 0
>
:
A(m 1, A(m, n 1)) se m > 0 e n > 0
Defina, em Lisp, a funo de Ackermann.
Exerccio 7.14.2 Indique o valor de
1. (ackermann 0 8)
2. (ackermann 1 8)
3. (ackermann 2 8)
4. (ackermann 3 8)
5. (ackermann 4 8)
Exerccio 7.14.3 Defina uma funo denominada escada que, dado um ponto P , um nmero de degraus, o comprimento do espelho e e o comprimento do cobertor c de cada
degrau, desenha a escada em AutoCad com o primeiro espelho a comear a partir do ponto
dado, tal como se v na imagem seguinte:

e
c
P
Exerccio 7.14.4 Defina uma funo equilibrio-circulos capaz de criar qualquer uma
das figuras apresentadas em seguida:

141

Note que os crculos possuem raios que esto em progresso geomtrica de razo f ,
com 0 < f < 1. Assim, cada crculo (excepto o primeiro) tem um raio que o produto de f
pelo raio do crculo maior em que est apoiado. O crculo mais pequeno de todos tem raio
maior ou igual a 1. A sua funo dever ter como parmetros o centro e o raio do crculo
maior e ainda o factor de reduo f .
Exerccio 7.14.5 Considere o desenho de crculos em torno de um centro, tal como apresentado na seguinte imagem:
y

r1
r0

Escreva uma funo denominada circulos-concentricos que, a partir das coordenadas p do centro de rotao, do nmero de crculos n, do raio de translaco r0 , do raio
de circunferncia r1 , do ngulo inicial e do incremento de ngulo , desenha os crculos
tal como apresentados na figura anterior.
Teste a sua funo com os seguintes expresses:
(circulos-concentricos (xy 0 0) 10 10 2 0 (/ pi 5))
(circulos-concentricos (xy 25 0) 20 10 2 0 (/ pi 10))
(circulos-concentricos (xy 50 0) 40 10 2 0 (/ pi 20))

cuja avaliao dever gerar a imagem seguinte:

Exerccio 7.14.6 Considere o desenho de flores simblicas compostas por um crculo interior em torno da qual esto dispostos crculos concntricos correspondentes a ptalas. Estes
crculos devero ser tangentes uns aos outros e ao crculo interior, tal como se apresenta na
seguinte imagem:

142

Defina a funo flor que recebe apenas o ponto correspondente ao centro da flor, o
raio do crculo interior e o nmero de ptalas.
Teste a sua funo com as expresses
(flor (xy 0 0) 5 10)
(flor (xy 18 0) 2 10)
(flor (xy 40 0) 10 20)

que devero gerar a imagem seguinte:

7.15

Depurao de Programas Recursivos

Vimos, na seco 4.19 que os erros de um programa se podem classificar em


termos de erros sintticos ou erros semnticos. Os erros sintticos ocorrem
quando escrevemos frases invlidas na linguagem. Como exemplo de erro
sinttico, consideremos a seguinte definio da funo potencia onde nos
esquecemos da lista de parmetros:
143

(defun potencia
(if (= n 0)
1
(* (potencia x (- n 1)) x)))

Este esquecimento o suficiente para baralhar o Auto Lisp: ele esperava


encontrar a lista de parmetros imediatamente a seguir a nome da funo
e que, no lugar dela, encontra uma lista que comea com o smbolo if. Isto
faz com que o Auto Lisp erradamente acredite que o smbolo if o nome
do primeiro parmetro e, da para a frente, a sua confuso s aumenta. A
dado momento, quando o Auto Lisp deixa, por completo, de compreender
aquilo que se pretendia definir, apresenta-nos um erro.
Os erros semnticos so mais complexos que os sintticos pois, em geral, s podem ser detectados durante a execuo do programa.45 Por exemplo, se tentarmos calcular o factorial de uma string iremos ter um erro, tal
como o seguinte exemplo mostra:
_$ (factorial 5)
120
_$ (factorial "cinco")
; error: bad argument type: numberp: "cinco"

Este ltimo erro, como bvio, no tem a ver com a regras da gramtica
do Lisp: a frase da invocao da funo factorial est correcta. O
problema est no facto de no fazer sentido calcular o factorial de uma
string pois o clculo do factorial envolve operaes aritmticas e estas no
so aplicveis a strings. Assim sendo, o erro tem a ver com o significado
da frase escrita, i.e., com a semntica. Trata-se, portanto, de um erro
semntico.
A recurso infinita outro exemplo de um erro semntico. Vimos que a
funo factorial s pode ser invocada com um argumento no negativo ou
provoca recurso infinita. Consequentemente, se usarmos um argumento
negativo estaremos a cometer um erro semntico.
7.15.1

Trace

Vimos, na seco4.19, que o Visual Lisp disponibiliza vrios mecanismos


para fazermos a deteco de erros. Um dos mais simples a forma trace
que permite visualizar as invocaes das funes. A forma trace recebe o
45

H casos de erros semnticos que podem ser detectados antes da execuo do programa
mas essa deteco depende muito da qualidade da implementao da linguagem e da sua
capacidade em antecipar as consequncias dos programas.

144

nome das funes cuja invocao se pretende analisar e altera essas funes
de forma a que elas escrevam as sucessivas invocaes com os respectivos
argumentos, bem como o resultado da invocao. Se o ambiente do Visual
Lisp estiver activo, a escrita feita numa janela especial do ambiente do
Visual Lisp denominada Trace,46 caso contrrio, feita na janela de comandos do AutoCad. A informao apresentada em resultado do trace ,
em geral, extremamente til para a depurao das funes.
Por exemplo, para visualizarmos uma invocao da funo factorial,
consideremos a seguinte interaco:
_$ (trace factorial)
FACTORIAL
_$ (factorial 5)
120

Em seguida, se consultarmos o contedo da janela de Trace, encontramos:


Entering (FACTORIAL 5)
Entering (FACTORIAL 4)
Entering (FACTORIAL 3)
Entering (FACTORIAL 2)
Entering (FACTORIAL 1)
Entering (FACTORIAL 0)
Result: 1
Result: 1
Result: 2
Result: 6
Result: 24
Result: 120

Note-se, no texto anterior escrito em consequncia do trace, que a invocao que fizemos da funo factorial aparece encostada esquerda.
Cada invocao recursiva aparece ligeiramente para a direita, permitindo
assim visualizar a profundidade da recurso, i.e., o nmero de invocaes recursivas. O resultado devolvido por cada invocao aparece alinhado na mesma coluna dessa invocao.
de salientar que a janela de Trace no tem dimenso infinita, pelo
que as recurses excessivamente profundas acabaro por no caber na janela. Neste caso, o Visual Lisp reposiciona a escrita na coluna esquerda
mas indica numericamente o nvel de profundidade em que se encontra.
Por exemplo, para o (factorial 15), aparece:
46

Para se visualizar a janela de Trace basta seleccion-la a partir do menu Window do


Auto Lisp.

145

Entering (FACTORIAL 15)


Entering (FACTORIAL 14)
Entering (FACTORIAL 13)
Entering (FACTORIAL 12)
Entering (FACTORIAL 11)
Entering (FACTORIAL 10)
Entering (FACTORIAL 9)
Entering (FACTORIAL 8)
Entering (FACTORIAL 7)
Entering (FACTORIAL 6)
[10] Entering (FACTORIAL 5)
[11]
Entering (FACTORIAL 4)
[12]
Entering (FACTORIAL 3)
[13]
Entering (FACTORIAL 2)
[14]
Entering (FACTORIAL 1)
[15]
Entering (FACTORIAL 0)
[15]
Result: 1
[14]
Result: 1
[13]
Result: 2
[12]
Result: 6
[11]
Result: 24
[10] Result: 120
Result: 720
Result: 5040
Result: 40320
Result: 362880
Result: 3628800
Result: 39916800
Result: 479001600
Result: 1932053504
Result: 1278945280
Result: 2004310016

No caso de recurses infinitas, apenas so mostradas as ltimas invocaes realizadas antes de ser gerado o erro. Por exemplo, para (factorial -1),
teremos:
...
[19215]
Entering (FACTORIAL -19216)
[19216]
Entering (FACTORIAL -19217)
[19217]
Entering (FACTORIAL -19218)
[19218]
Entering (FACTORIAL -19219)
[19219]
Entering (FACTORIAL -19220)
[19220] Entering (FACTORIAL -19221)
[19221]
Entering (FACTORIAL -19222)
[19222]
Entering (FACTORIAL -19223)
[19223]
Entering (FACTORIAL -19224)
[19224]
Entering (FACTORIAL -19225)

146

...
[19963]
Entering (FACTORIAL -19964)
[19964]
Entering (FACTORIAL -19965)
[19965]
Entering (FACTORIAL -19966)
[19966]
Entering (FACTORIAL -19967)
[19967]
Entering (FACTORIAL -19968)
[19968]
Entering (FACTORIAL -19969)
[19969]
Entering (FACTORIAL -19970)
[19970] Entering (FACTORIAL -19971)
[19971]
Entering (FACTORIAL -19972)
[19972]
Entering (FACTORIAL -19973)
[19973]
Entering (FACTORIAL -19974)
[19974]
Entering (FACTORIAL -19975)
[19975]
Entering (FACTORIAL -19976)

Para se parar a depurao de uma funo, usa-se a forma especial untrace,


que recebe o nome da funo ou funes que se pretende retirar do trace.
Exerccio 7.15.1 Faa o trace da funo potncia. Qual o trace resultante da avaliao
(potencia 2 10)?
Exerccio 7.15.2 Defina uma funo circulos capaz de criar a figura apresentada em seguida:

Note que os crculos possuem raios que esto em progresso geomtrica de razo 12 .
Dito de outra forma, os crculos mais pequenos tm metade do raio do crculo maior que
lhes adjacente. Os crculos mais pequenos de todos tm raio maior ou igual a 1. A sua
funo dever ter como parmetros apenas o centro e o raio do crculo maior.
Exerccio 7.15.3 Defina uma funo denominada serra que, dado um ponto P , um nmero de dentes, o comprimento c de cada dente e a altura a de cada dente, desenha uma
serra em AutoCad com o primeiro dente a comear a partir do ponto P , tal como se v na
imagem seguinte:

147

a
P
c
Exerccio 7.15.4 Defina uma funo losangulos capaz de criar a figura apresentada em
seguida:

Note que os losngulos possuem dimenses que esto em progresso geomtrica de


razo 12 . Dito de outra forma, os losngulos mais pequenos tm metade do tamanho do
losngulo maior em cujas extremidades esto centrados. Os losngulos mais pequenos de
todos tm largura maior ou igual a 1. A sua funo dever ter como parmetros apenas o
centro e a largura do losngulo maior.

7.16

Templos Dricos

Vimos, pelas descries de Vitrvio, que os Gregos criaram um elaborado


sistema de propores para colunas. Estas colunas eram usadas para a
criao de prticos, em que uma sucesso de colunas encimadas por um
telhado servia de entrada para os edifcios e, em particular, para os templos. Quando esse arranjo de colunas era avanado em relao ao edifcio,
denominava-se o mesmo de prstilo, classificando-se este pelo nmero de
colunas que possuem em Distilo, Tristilo, Tetrastilo, Pentastilo, Hexastilo,
etc. Quando o prstilo se alargava a todo o edifcio, colocando colunas a
toda a sua volta, denominava-se de peristilo.
Para alm de descrever as propores das colunas, Vitrvio tambm
explicou no seu famoso tratado as regras que se deviam seguir para a separao entre colunas, distinguindo vrios casos de templos, desde aqueles
em que o espao entre colunas era muito reduzido (picnostilo) at aos templos com espaamento excessivamente alargado (araeostilo), passando pelo
seu favorito (eustilo) em que o espao entre colunas varivel, sendo maior
nas colunas centrais.

148

Na prtica, nem sempre as regras propostas por Vitrvio representavam a realidade dos templos e, frequentemente, a distancia intercolunar
varia, sendo mais reduzida nas extremidades para dar um aspecto mais
robusto ao templo.
Para simplificar a nossa implementao, vamos ignorar estes detalhes
e, ao invs de distinguir vrios stilo, vamos simplesmente usar como parmetros as coordenadas da base do eixo da primeira coluna, a altura da
coluna, a separao entre os eixos das colunas47 (em termos de um vector de deslocao entre colunas) e, finalmente, o nmero de colunas que
pretendemos colocar. O vector de deslocao entre colunas destina-se a
permitir orientar as colunas em qualquer direco e no s ao longo do eixo
dos x ou y.
O raciocnio para a definio desta funo , mais uma vez, recursivo:
Se o nmero de colunas a colocar for zero, ento no preciso fazer
nada e podemos simplesmente retornar nil.
Caso contrrio, colocamos uma coluna na coordenada indicada e, recursivamente, colocamos as restantes colunas a partir da coordenada
que resulta de aplicarmos o vector de deslocao coordenada actual. Como so duas aces que queremos realizar sequencialmente,
teremos de usar o operador progn para as agrupar numa aco conjunta.
Traduzindo este raciocnio para Lisp, temos:
(defun colunas-doricas (p altura separacao colunas)
(if (= colunas 0)
nil
(progn
(coluna-dorica p altura)
(colunas-doricas (+c p separacao)
altura
separacao
(- colunas 1)))))

Finalmente, podemos testar a criao das colunas usando, por exemplo:


(colunas-doricas (xy 0 0) 10 (xy 5 0) 8)

cujo resultado est apresentado na Figura 30:


47

A denominada distncia interaxial, por oposio distncia intercolunar que mede o


espao aberto entre colunas adjacentes.

149

Figura 30: Uma perspectiva de um conjunto de oito colunas dricas com 10


unidades de altura e 5 unidades de separao entre os eixos da colunas ao
longo do eixo x.

150

A partir do momento em que sabemos construir uma fileira de colunas torna-se relativamente fcil a construo das quatro fileiras necessrias
para os templos em peristilo. Normalmente, a descrio destes templos
faz-se em termos do nmero de colunas da fronte e do nmero de colunas
do lado, mas assumindo que as colunas dos cantos contam para ambas as
medidas. Isto quer dizer que num templo de, por exemplo, 6 12 colunas
existem, na realidade, apenas 4 2 + 10 2 + 4 = 32 colunas. Para a construo do peristilo, para alm do nmero de colunas das frontes e lados,
ser necessrio conhecer a distncia entre colunas nas frontes e nos lados e,
claro, a altura das colunas.
Em termos de algoritmo, vamos comear por construir as frontes, desenhando todas as colunas incluindo os cantos. Em seguida construmos os
lados, tendo em conta que os cantos j foram colocados. Para simplificar,
vamos considerar que o templo tem as frontes paralelas ao eixo x (e os lados paralelos ao eixo y) e que a primeira coluna colocada num ponto P
dado como primeiro parmetro. Assim, temos:
(defun peristilo-dorico (p
n-fronte n-lado
d-fronte d-lado
altura)
(colunas-doricas p
altura (xy d-fronte 0) n-fronte)
(colunas-doricas (+xy p 0 (* (1- n-lado) d-lado))
altura (xy d-fronte 0) n-fronte)
(colunas-doricas (+xy p 0 d-lado)
altura (xy 0 d-lado) (- n-lado 2))
(colunas-doricas (+xy p (* (1- n-fronte) d-fronte) d-lado)
altura (xy 0 d-lado) (- n-lado 2)))

Para um exemplo realista podemos considerar o templo de Segesta que


se encontra representado da Figura 12. Este templo do tipo peristilo, composto por 6 colunas (i.e., Hexastilo) em cada fronte e 14 colunas nos lados,
num total de 36 colunas de 9 metros de altura. A distncia entre os eixos
das colunas de aproximadamente 4.8 metros nas frontes e de 4.6 nos lados. A expresso que cria o peristilo deste templo , ento:
(peristilo-dorico (xy 0 0) 6 14 4.8 4.6 9)

O resultado da avaliao da expresso anterior est representado na


Figura 31
Embora a grande maioria dos templos Gregos fossem de formato rectangular, tambm foram construdos templos de formato circular a que chamaram Tholos. O Santurio de Atenas Pronaia, em Delfos, contm um bom
151

Figura 31: Uma perspectiva do peristilo do templo de Segesta. As colunas


foram geradas pela funo peristilo-dorico usando, como parmetros, 6 colunas na fronte e 14 no lado, com distncia intercolunar de 4.8
metros na fronte e 4.6 metros no lado e colunas de 9 metros de altura.

152

Figura 32: O Templo de Atenas Pronaia em Delfos, construdo no sculo


quarto antes de Cristo. Fotografia de Michelle Kelley
exemplo de um destes edifcios. Embora pouco reste deste templo, no
difcil imaginar a sua forma original a partir do que ainda visvel na
Figura 32.
Para simplificar a construo do Tholos, vamos dividi-lo em duas partes.
Numa, iremos desenhar a base e, na outra, iremos posicionar as colunas.
Para o desenho da base, podemos considerar um conjunto de cilindros
achatados, sobrepostos de modo a formar degraus circulares, tal como se
apresenta na Figura 33. Desta forma, a altura total da base ab ser dividida
em passos de ab e o raio da base tambm ser reduzido em passos de rb .
Para cada cilindro teremos de considerar o seu raio e a altura do espelho do degrau d-altura. Para passarmos ao cilindro seguinte temos
ainda de ter em conta o aumento do raio d-raio devido ao comprimento
do cobertor do degrau. Estes degraus sero construdos segundo um processo recursivo:
Se o nmero de degraus a colocar zero, no preciso fazer nada.
Caso contrrio, colocamos um degrau (modelado por um cilindro)
com o raio e a altura do degrau e, recursivamente, colocamos os restantes degraus em cima deste, i.e., numa cota igual altura do degrau
153

rp

ab
rb
x
rb
Figura 33: Corte da base de um Tholos. A base composta por uma sequncia de cilindros sobrepostos cujo raio de base rb encolhe de rb a cada degrau e cuja altura incrementa ab a cada degrau.

154

agora colocado e com um raio reduzido do comprimento do cobertor


do degrau agora colocado.
Este processo implementado pela seguinte funo:
(defun base-tholos (p n-degraus raio d-altura d-raio)
(if (= n-degraus 0)
nil
(progn
(command "_.cylinder" p raio d-altura)
(base-tholos (+xyz p 0 0 d-altura)
(- n-degraus 1)
(- raio d-raio)
d-altura
d-raio))))

Para o posicionamento das colunas, vamos tambm considerar um processo em que em cada passo apenas posicionamos uma coluna numa dada
posio e, recursivamente, colocamos as restantes colunas a partir da posio circular seguinte.
Dada a sua estrutura circular, a construo deste gnero de edifcios
simplificada pelo uso de coordenadas circulares. De facto, podemos conceber um processo recursivo que, a partir do raio rp do peristilo e do ngulo
inicial , coloca uma coluna nessa posio e que, de seguida, coloca as restantes colunas usando o mesmo raio mas incrementando o ngulo de
, tal como se apresenta na Figura 34. O incremento angular obtmse pela diviso da circunferncia pelo nmero n de colunas a colocar, i.e.,
= 2
n . Uma vez que as colunas se dispem em torno de um crculo,
o clculo das coordenadas de cada coluna fica facilitado pelo uso de coordenadas polares. Tendo este algoritmo em mente, a definio da funo
fica:
(defun colunas-tholos (p n-colunas raio fi d-fi altura)
(if (= n-colunas 0)
nil
(progn
(coluna-dorica (+pol p raio fi) altura)
(colunas-tholos p
(1- n-colunas)
raio
(+ fi d-fi)
d-fi
altura))))

Finalmente, definimos a funo tholos que, dados os parmetros necessrios s duas anteriores, as invoca em sequncia:
155

ap
ab
rb

rp
y

rp

x
Figura 34: Esquema da construo de um Tholos: rb o raio da base, rp a
distncia do centro das colunas ao centro da base, ap a altura das coluna,
ab a altura da base, o ngulo inicial de posicionamento das colunas e
o ngulo entre colunas.

156

Figura 35: Perspectiva do Tholos de Atenas em Delfos, constitudo por 20


colunas de estilo Drico, de 4 metros de altura e colocadas num crculo com
7 metros de raio.
(defun tholos (p n-degraus rb dab drb n-colunas rp ap)
(base-tholos p n-degraus rb dab drb)
(colunas-tholos (+xyz p 0 0 (* n-degraus dab))
n-colunas
rp
0
(/ 2*pi n-colunas)
ap))

A Figura 35 mostra a imagem gerada pela avaliao da seguinte expresso:


(tholos (xyz 0 0 0) 3 7.9 0.2 0.2 20 7 4)

Exerccio 7.16.1 Uma observao atenta do Tholos apresentado na Figura 35 mostra que
existe um erro: os bacos das vrias colunas so paralelos uns aos outros (e aos eixos das
abcissas e ordenadas) quando, na realidade, deveriam ter uma orientao radial. Essa diferena evidente quando se compara uma vista de topo do desenho actual ( esquerda) com
a mesma vista daquele que seria o desenho correcto ( direita):

157

Defina uma nova funo coluna-dorica-rodada que, para alm da altura da coluna, recebe ainda o ngulo de rotao que a coluna deve ter. Uma vez que o fuste e o
coxim da coluna tm simetria axial, esse ngulo de rotao s influencia o baco, pelo que
dever tambm definir uma funo para desenhar um baco rodado.
De seguida, redefina a funo colunas-tholos de modo a que cada coluna esteja
orientada correctamente relativamente ao centro do Tholos.
Exerccio 7.16.2 Considere a construo de uma torre composta por vrios mdulos em
que cada mdulo tem exactamente as mesmas caractersticas de um Tholos, tal como se
apresenta na figura abaixo, esquerda:

O topo da torre tem uma forma semelhante da base de um Tholos, embora com mais
degraus.

158

Defina a funo torre-tholos que, a partir do centro da base da torre, do nmero


de mdulos, do nmero de degraus a considerar para o topo e dos restantes parmetros
necessrios para definir um mdulo idntico a um Tholos, constri a torre apresentada anteriormente.
Experimente a sua funo criando uma torre composta por 6 mdulos, com 10 degraus
no topo, 3 degraus por mdulo, qualquer deles com comprimento de espelho e de cobertor
de 0.2, raio da base de 7.9 e 20 colunas por mdulo, com raio de peristilo de 7 e altura de
coluna de 4.
Exerccio 7.16.3 Com base na resposta ao exerccio anterior, redefina a construo da torre
de forma a que a dimenso radial dos mdulos se v reduzindo medida que se ganha
altura, tal como acontece na torre apresentada no centro da imagem anterior.
Exerccio 7.16.4 Com base na resposta ao exerccio anterior, redefina a construo da torre
de forma a que o nmero de colunas se v tambm reduzindo medida que se ganha altura,
tal como acontece na torre da direita da imagem anterior.

Exerccio 7.16.5 Considere a criao de uma cidade no espao, composta apenas por cilindros com dimenses progressivamente mais pequenas, unidos uns aos outros por intermdio de esferas, tal como se apresenta (em perspectiva) na imagem seguinte:

159

Defina uma funo que, a partir do centro da cidade e do raio dos cilindros centrais
constri uma cidade semelhante representada.

7.17

A Ordem Jnica

A voluta foi um dos elementos arquitectnicos introduzidos na transio da


Ordem Drica para a Ordem Jnica. Uma voluta um ornamento em forma
de espiral colocado em cada extremo de um capitel Jnico. A Figura 36
mostra um exemplo de um capitel Jnico contendo duas volutas. Embora
tenham sobrevivido inmeros exemplos de volutas desde a antiguidade,
nunca foi claro o processo do seu desenho.
Vitrvio, no seu tratado de arquitectura, descreve a voluta Jnica: uma
curva em forma de espiral que se inicia na base do baco, desenrola-se
numa srie de voltas e junta-se a um elemento circular denominado o olho.
Vitrvio descreve o processo de desenho da espiral atravs da composio de quartos de circunferncia, comeando pelo ponto mais exterior e
diminuindo o raio a cada quarto de circunferncia, at se dar a conjuno
com o olho. Nesta descrio h ainda alguns detalhes por explicar, em particular, o posicionamento dos centros dos quartos de circunferncia, mas
Vitrvio refere que ser incluida uma figura e um clculo no final do livro.
Infelizmente, nunca se encontrou essa figura ou esse clculo, ficando
assim por esclarecer um elemento fundamental do processo de desenho de
160

Figura 36: Volutas de um capitel Jnico. Fotografia de See Wah Cheng.


volutas descrito por Vitrvio. As dvidas sobre esse detalhe tornaram-se
ainda mais evidentes quando a anlise de muitas das volutas que sobreviveram a antiguidade revelou diferenas em relao s propores descritas
por Vitrvio.
Durante a Renascena, estas dvidas levaram os investigadores a repensar o mtodo de Vitrvio e a sugerir interpretaes pessoais ou novos
mtodos para o desenho da voluta. De particular relevncia foram os mtodos propostos por:
Sebastiano Serlio (1537), baseado na composio de semi-circunferncias,
Giuseppe Salviati (1552), baseado na composio de quartos-de-circunferncia e
Guillaume Philandrier (1544), baseado na composio de oitavos-decircunferncia.
Todos estes mtodos diferem em vrios detalhes mas, de forma genrica, todos se baseiam em empregar arcos de circunferncia de abertura
constante mas raio decrescente. Obviamente, para que haja continuidade
nos arcos, os centros dos arcos vo mudando medida que estes vo sendo
desenhados. A Figura 37 esquematiza o processo para espirais feitas empregando quartos de circunferncia.

161

P1 ~v0

rf

P
~v1 ~v
rf f f
2
P2
P3

rf f

Figura 37: O desenho de uma espiral com arcos de circunferncia.

162

Como se v pela figura, para se desenhar a espiral temos de ir desenhando sucessivos quartos de circunferncia. O primeiro quarto de circunferncia ser centrado no ponto P e ter raio r. Este primeiro arco vai desde
o ngulo /2 at ao ngulo . O segundo quarto de circunferncia ser centrado no ponto P1 e ter raio r f , sendo f um coeficiente de reduo da
espiral. Este segundo arco vai desde o ngulo at ao ngulo 23 . Um detalhe importante a relao entre as coordenadas P1 e P : para que o segundo
arco tenha uma extremidade coincidente com o primeiro arco, o seu centro
tem de estar na extremidade do vector ~v0 de origem em P , comprimento
r(1 f ) e ngulo igual ao ngulo final do primeiro arco.
Este processo dever ser seguido para todos os restantes arcos de circunferncia, i.e., teremos de calcular as coordenadas P2 , P3 , etc., bem como
os raios r f f , r f f f , etc, necessrios para traar os sucessivos arcos
de circunferncia.
Dito desta forma, o processo de desenho parece ser complicado. No
entanto, possivel reformul-lo de modo a ficar muito mais simples. De
facto, podemos pensar no desenho da espiral completa como sendo o desenho de um quarto de circunferncia seguido do desenho de uma espiral
mais pequena. Mais concretamente, podemos especificar o desenho da espiral de centro no ponto P , raio r e ngulo inicial como sendo o desenho
de um arco de circunferncia de raio r centrado em P com ngulo inicial
e final + 2 seguido de uma espiral de centro em P + ~v , raio r f e ngulo
inicial + 2 . O vector ~v ter origem em P , mdulo r(1 f ) e ngulo + 2 .
Obviamente, sendo este um processo recursivo, necessrio definir o
caso de paragem, havendo (pelo menos) duas possibilidades:
Terminar quando o raio r inferior a um determinado limite.
Terminar quando o ngulo superior a um determinado limite.
Por agora, vamos considerar a segunda possibilidade. De acordo com o
nosso raciocnio, vamos definir a funo que desenha a espiral de modo a
receber, como parmetros, o ponto inicial p, o raio inicial r, o ngulo inicial
a-ini, o ngulo final a-fin e o factor de reduo f:
(defun espiral (p r a-ini a-fin f)
(if (> a-ini a-fin)
nil
(progn
(quarto-circunferencia p r a-ini)
(espiral (+pol p (* r (- 1 f)) (+ a-ini (/ pi 2)))
(* r f)

163

(+ a-ini (/ pi 2))
a-fin
f))))

Reparemos que a funo espiral recursiva pois est definida em


termos de si prpria. Obviamente, o caso recursivo mais simples que o
caso original pois a diferena entre o ngulo inicial e o final mais pequena,
aproximando-se progressivamente do caso de paragem em que o ngulo
inicial ultrapassa o final.
Para desenhar o quarto de circunferncia vamos empregar a operao
arc do Auto Lisp que recebe o centro do circunferncia, o ponto inicial do
arco e o ngulo em graus. Para melhor percebermos o processo de desenho da espiral vamos tambm traar duas linhas com origem no centro a
delimitar cada quarto de circunferncia. Mais tarde, quando tivermos terminado o desenvolvimento destas funes, removeremos essas linhas.
Desta forma, o quarto de circunferncia apenas precisa de saber o ponto
p correspondente ao centro da circunferncia, o raio r da mesma e o ngulo
inicial a-ini:
(defun quarto-circunferencia (p
(command "_.arc" "_c" p (+pol
(command "_.line" p (+pol p r
(command "_.line" p (+pol p r

r a-ini)
p r a-ini) "_a" 90)
a-ini) "")
(+ a-ini (/ pi 2))) ""))

Podemos agora experimentar um exemplo:


(espiral (xy 0 0) 10 (/ pi 2) (* pi 6) 0.8)

A espiral traada pela expresso anterior est representada na Figura 38.


A funo espiral permite-nos definir um sem-nmero de espirais,
mas tem uma restrio: cada arco de crculo corresponde a um ngulo de

2 . Logicamente, a funo tornar-se- mais til se tambm este incremento


de ngulo for um parmetro.
As modificaes a fazer so relativamente triviais, bastando acrescentar um parmetro a-inc representando o incremento de ngulo de cada
arco e substituir as ocorrncias de (/ pi 2) por este parmetro. Naturalmente, em vez de desenharmos um quarto de circunferncia, temos agora
de desenhar um arco de circunferncia.
A nova definio , ento:
(defun espiral (p r a-ini a-inc a-fin f)
(if (> a-ini a-fin)
nil

164

Figura 38: O desenho da espiral.


(progn
(arco p r a-ini a-inc)
(espiral (+pol p (* r (- 1 f)) (+ a-ini a-inc))
(* r f)
(+ a-ini a-inc)
a-inc
a-fin
f))))

A funo que desenha o arco uma variante da que desenha o quarto


de circunferncia. Apenas preciso ter em conta que o comando arc pretende o ngulo em graus e no em radianos, o que nos obriga a usar uma
converso:
(defun arco (p r a-ini a-inc)
(command "_.arc"
"_c" p (+pol p r a-ini)
"_a" (graus<-radianos a-inc))
(command "_.line" p (+pol p r a-ini) "")
(command "_.line" p (+pol p r (+ a-ini a-inc)) ""))
(defun graus<-radianos (radianos)
(* (/ 180 pi) radianos))

Agora, para desenhar a mesma espiral representada na Figura 38, temos


de avaliar a expresso:
165

Figura 39: Vrias espirais com razes de reduo de 0.9, 0.7 e 0.5, respectivamente.
(espiral (xy 0 0) 10 (/ pi 2) (/ pi 2) (* pi 6) 0.8)

claro que agora podemos facilmente construir outras espirais. As seguintes expresses produzem as espirais representadas na Figura 39:
(espiral (xy 0 0) 10 (/ pi 2) (/ pi 2) (* pi 6) 0.9)
(espiral (xy 20 0) 10 (/ pi 2) (/ pi 2) (* pi 6) 0.7)
(espiral (xy 40 0) 10 (/ pi 2) (/ pi 2) (* pi 6) 0.5)

Outra possibilidade de variao est no ngulo de incremento. As seguintes expresses experimentam aproximaes aos processos de Sebastiano Serlio (semi-circunferncias), Giuseppe Salviati (quartos-de-circunferncia)
e Guillaume Philandrier (oitavos-de-circunferncia):48
(espiral (xy 0 0) 10 (/ pi 2) pi (* pi 6) 0.8)
(espiral (xy 20 0) 10 (/ pi 2) (/ pi 2) (* pi 6) 0.8)
(espiral (xy 40 0) 10 (/ pi 2) (/ pi 4) (* pi 6) 0.8)

Os resultados esto representados na Figura 40.


48

Note-se que se trata, to somente, de aproximaes. Os processos originais eram bastante mais complexos.

166

Figura 40: Vrias espirais com razo de reduo de 0.8 e incremento de


ngulo de , 2 e 4 , respectivamente.
Exerccio 7.17.1 Um vulo uma figura geomtica que corresponde ao contorno dos ovos
das aves. Dada a variedade de formas de ovos na Natureza, natural considerar que tambm existe uma grande variedade de vulos geomtrivos. A figura seguinte apresenta alguns exemplos em que se variam sistematicamente alguns dos parmetros que caracterizam o vulo:

Um vulo composto por quatro arcos de circunferncia concordantes, tal como se


apresenta na imagem seguinte:

167

r1

r2

r2

r0

Os arcos de crculos necessrios para a construo do ovo so caracterizados pelos raios


r0 , r1 e r2 . Note que o arco de circunferncia de raio r0 cobre um ngulo de e o arco de
circunferncia de raio r2 cobre um ngulo de .
Defina uma funo ovo que desenha um ovo. A funo dever receber, apenas, as
coordenadas do ponto P , os raios r0 e r1 e, finalmente, a altura h do ovo.

7.18

Recurso na Natureza

A recurso est presente em inmeros fenmenos naturais. As montanhas,


por exemplo, apresentam irregularidades que, quando observadas numa
escala apropriada, so em tudo idnticas a . . . montanhas. Um rio possui afluentes e cada afluente idntico a . . . um rio. Uma vaso sanguneo
possui ramificaes e cada ramificao idntica a . . . um vaso sanguneo.
Todas estas entidades naturais constituem exemplos de estruturas recursivas.
Uma rvore outro bom exemplo de uma estrutura recursiva pois os
ramos de uma rvore so como pequenas rvores que emanam do tronco.
Como se pode ver da Figura 41, de cada ramo de uma rvore emanam
outras pequenas rvores, num processo que se repete at se atingir uma dimenso suficientemente pequena em que aparecem outras estruturas como
folhas, flores, frutos, pinhas, etc.
Se, de facto, uma rvore possui uma estrutura recursiva ento dever
ser possvel construir rvores atravs de funes recursivas. Para tes168

Figura 41: A estrutura recursiva das rvores. Fotografia de Michael Bezzina.

169

f c

f c
c

(x0 , y0 )
Figura 42: Parmetros de desenho de uma rvore.
tarmos esta teoria, vamos comear por considerar uma verso muito simplista de uma rvore, em que temos um tronco que, a partir de uma certa
altura, se divide em dois. Cada um destes subtroncos cresce fazendo um
certo ngulo com o tronco de onde emanou e com um comprimento que
dever ser uma fraco do comprimento desse tronco, tal como se apresenta na Figura 42. O caso de paragem ocorre quando o comprimento do
tronco se tornou to pequeno que, em vez de se continuar a diviso, aparece simplesmente uma outra estrutura. Para simplificar, vamos designar a
extremidade de um ramo por folha e iremos represent-la com um pequeno
crculo.
Para darmos dimenses rvore, vamos considerar que a funo arvore
recebe, como argumento, as coordenadas da base da rvore, o comprimento
do tronco e o ngulo actual do tronco. Para a fase recursiva, teremos como
parmetros o ngulo de abertura alpha que o novo tronco dever fazer
com o actual e o factor de reduo f do comprimento do tronco. O primeiro passo a computao do topo do tronco usando a funo +pol. Em
seguida, desenhamos o tronco desde a base at ao topo. Finalmente, testamos se o tronco desenhado suficientemente pequeno. Se for, terminamos
com o desenho de um crculo centrado no topo. Caso contrrio fazemos
uma dupla recurso para desenhar uma sub-rvore para a direita e outra
para a esquerda. A definio da funo fica:
(defun arvore (base comprimento angulo alfa f / topo)
(setq topo (+pol base comprimento angulo))
(ramo base topo)
(if (< comprimento 2)
(folha topo)
(progn

170

Figura 43: Uma rvore de comprimento 20, ngulo inicial 2 , abertura


e factor de reduo 0.7.

(arvore topo
(* comprimento f)
(+ angulo alfa)
alfa f)
(arvore topo
(* comprimento f)
(- angulo alfa)
alfa f))))
(defun ramo (base topo)
(command "_.line" base topo ""))
(defun folha (topo)
(command "_.circle" topo 0.2))

Um primeiro exemplo de rvore gerado com a expresso


(arvore (xy 0 0) 20 (/ pi 2) (/ pi 8) 0.7)

est representado na Figura 43.


A Figura 44 apresenta outros exemplos em que se fez variar o ngulo
de abertura e o factor de reduo. A sequncia de expresses que as gerou
foi a seguinte:
(arvore (xy 0 0) 20 (/ pi 2) (/ pi 8) 0.6)
(arvore (xy 100 0) 20 (/ pi 2) (/ pi 8) 0.8)

171

Figura 44: Vrias rvores geradas com diferentes ngulos de abertura e


factores de reduo do comprimento dos ramos.

(arvore (xy 200 0) 20 (/ pi 2) (/ pi 6) 0.7)


(arvore (xy 300 0) 20 (/ pi 2) (/ pi 12) 0.7)

Infelizmente, as rvores apresentadas so excessivamente simtricas:


no mundo natural literalmente impossvel encontrar simetrias perfeitas.
Por este motivo, convm tornar o modelo um pouco mais sofisticado atravs da introduo de parmetros diferentes para o crescimento dos troncos
direita e esquerda. Para isso, em vez de termos um s ngulo de abertura e um s factor de reduo de comprimento, vamos empregar dois, tal
como se apresenta na Figura 45.
A adaptao da funo arvore para lidar com os parmetros adicionais trivial:
(defun arvore (base comprimento angulo
alfa-e f-e alfa-d f-d
/ topo)
(setq topo (+pol base comprimento angulo))
(ramo base topo)
(if (< comprimento 2)
(folha topo)
(progn
(arvore topo
(* comprimento f-e)

172

fe c

fd c
c

(x0 , y0 )
Figura 45: Parmetros de desenho de uma rvore com crescimento assimtrico.
(+ angulo alfa-e)
alfa-e f-e alfa-d f-d)
(arvore topo
(* comprimento f-d)
(- angulo alfa-d)
alfa-e f-e alfa-d f-d))))

A Figura 46 apresenta novos exemplos de rvores com diferentes ngulos de abertura e factores de reduo dos ramos esquerdo e direito, geradas
pelas seguintes expresses:
(arvore (xy 0 0) 20 (/ pi 2) (/ pi 8) 0.6 (/ pi 8) 0.7)
(arvore (xy 100 0) 20 (/ pi 2) (/ pi 4) 0.7 (/ pi 16) 0.7)
(arvore (xy 200 0) 20 (/ pi 2) (/ pi 6) 0.6 (/ pi 16) 0.8)

As rvores geradas pela funo arvore so apenas um modelo muito


grosseiro da realidade. Embora existam sinais evidentes de que vrios fenmenos naturais se podem modelar atravs de funes recursivas, a natureza no to determinista quanto as nossas funes e, para que a modelao se aproxime mais da realidade, fundamental incorporar tambm
alguma aleatoriedade. Esse ser o tema da prxima secco.

173

Figura 46: Vrias rvores geradas com diferentes ngulos de abertura e


factores de reduo do comprimento para os ramos esquerdo e direito.

174

Funo
+

Argumentos
Vrios nmeros

Vrios nmeros

Vrios nmeros

Vrios nmeros

1+
1abs
sin
cos
atan

Um nmero
Um nmero
Um nmero
Um nmero
Um nmero
Um ou dois nmeros

sqrt

Um nmero no
negativo
Um nmero
Dois nmeros

exp
expt

Um nmero positivo
Vrios nmeros
Vrios nmeros
Dois ou mais
nmeros

log
max
min
rem

fix
float
gcd

Um nmero
Um nmero
Dois nmeros

Resultado
A adio de todos os argumentos. Sem argumentos, zero.
Com apenas um argumento, o seu simtrico. Com mais de um argumento, a subtraco ao primeiro de todos os restantes.
Sem argumentos, zero.
A multiplicao de todos os argumentos.
Sem argumentos, zero.
A diviso do primeiro argumento por todos os restantes. Sem argumentos, zero.
A soma do argumento com um.
A substraco do argumento com um.
O valor absoluto do argumento.
O seno do argumento (em radianos).
O cosseno do argumento (em radianos).
Com um argumento, o arco tangente do argumento (em radianos). Com dois argumentos, o arco tangente da diviso do primeiro pelo segundo (em radianos). O sinal
dos argumentos usado para determinar o
quadrante.
A raiz quadrada do argumento.
A exponencial de base e.
O primeiro argumento elevado ao segundo
argumento.
O logaritmo natural do argumento.
O maior dos argumentos.
O menor dos argumentos.
Com dois argumentos, o resto da diviso
do primeiro pelo segundo. Com mais argumentos, o resto da diviso do resultado
anterior pelo argumento seguinte.
O argumento sem a parte fraccionria.
O argumento convertido em nmero real.
O maior divisor comum dos dois argumentos.

Tabela 2: Funes matemticas pr-definidas do Auto Lisp.


175

Auto Lisp
(+ x0 x1 ...
(+ x)
(+)
(- x0 x1 ...
(- x)
(-)
(* x0 x1 ...
(* x)
(*)
(/ x0 x1 ...
(/ x)
(/)
(1+ x)
(1- x)
(abs x)
(sin x)
(cos x)
(atan x)
(atan x y)
(sqrt x)
(exp x)
(expt x y)
(log x)
(fix x)

xn )

xn )

xn )

xn )

Matemtica
x0 + x1 + . . . + xn
x
0
x0 x1 . . . xn
x
0
x0 x1 . . . xn
x
0
x0 /x1 / . . . /xn
x
0
x+1
x1
|x|
sin x
cos x
atan x
atan xy

x
x
e
xy
log x
bxc

Tabela 3: Funes matemticas pr-definidas do Auto Lisp.

176

Funo
strcat

Argumentos
Vrias strings

strlen

Vrias strings

substr

Uma
string,
um inteiro (o
ndice) e, opcionalmente,
outro
inteiro
(o nmero de
caracteres)
Uma string e
um
boleano
opcional

strcase

atof

Uma string

atoi

Uma string

itoa
rtos

Um nmero
Um, dois ou trs
nmeros

Resultado
A concatenao de todos os argumentos.
Sem argumentos, a string vazia .
O nmero de caracteres da concatenao
das strings. Sem argumentos, devolve
zero.
A parte da string que comea no ndice
dado e que tem o nmero de caracteres
dado ou todos os restantes caracteres no
caso de esse nmero no ter sido dado.

Com um argumento ou segundo argumento nil, a converso para maisculas


do argumento, caso contrrio, converso
para minsculas.
O nmero real cuja representao textual
o argumento.
O nmero, sem a parte fraccionria, cuja
representao textual o argumento.
A representao textual do argumento.
A representao textual do argumento,
de acordo com o modo especificado no
segundo argumento e com a preciso especificada no terceiro argumento.

Tabela 4: Operaes pr-definidas envolvendo strings.

177

S-ar putea să vă placă și