Vous êtes sur la page 1sur 288

Introduo Programao com AutoLisp

Antnio Menezes Leito


Fevereiro 2007

Contedo
1

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

A Linguagem Auto Lisp


2.1 Sintaxe, Semntica e Pragmtica . . . . . . . . .
2.2 Sintaxe e Semntica do Lisp . . . . . . . . . . . .
2.3 O Avaliador . . . . . . . . . . . . . . . . . . . . .
2.4 Elementos da Linguagem . . . . . . . . . . . . .
2.5 Nmeros . . . . . . . . . . . . . . . . . . . . . . .
2.6 Combinaes . . . . . . . . . . . . . . . . . . . .
2.7 Indentao . . . . . . . . . . . . . . . . . . . . . .
2.8 Avaliao de Combinaes . . . . . . . . . . . . .
2.9 Cadeias de Caracteres . . . . . . . . . . . . . . .
2.10 Operadores de Strings . . . . . . . . . . . . . . .
2.11 Definio de Funes . . . . . . . . . . . . . . . .
2.12 Smbolos . . . . . . . . . . . . . . . . . . . . . . .
2.13 Funes de Mltiplos Parmetros . . . . . . . . .
2.14 Encadeamento de Funes . . . . . . . . . . . . .
2.15 Funes Pr-Definidas . . . . . . . . . . . . . . .
2.16 Aritmtica de Inteiros em Auto Lisp . . . . . . .
2.17 Aritmtica de Reais em Auto Lisp . . . . . . . .
2.18 Avaliao de Smbolos . . . . . . . . . . . . . . .
2.19 Smbolos como Valores . . . . . . . . . . . . . . .
2.20 Expresses Condicionais . . . . . . . . . . . . . .
2.21 Expresses Lgicas . . . . . . . . . . . . . . . . .
2.22 Valores Lgicos . . . . . . . . . . . . . . . . . . .
2.23 Predicados . . . . . . . . . . . . . . . . . . . . . .
2.24 Predicados Aritmticos . . . . . . . . . . . . . . .
2.25 Operadores Lgicos . . . . . . . . . . . . . . . . .
2.26 Predicados com nmero varivel de argumentos
2.27 Predicados sobre Cadeias de Caracteres . . . . .
2.28 Reconhecedores . . . . . . . . . . . . . . . . . . .
2.29 Reconhecedores Universais . . . . . . . . . . . .
2.30 Exerccios . . . . . . . . . . . . . . . . . . . . . . .
2.31 Seleco . . . . . . . . . . . . . . . . . . . . . . .
2.32 Seleco MltiplaA Forma cond . . . . . . . .

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

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

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

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

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

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

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

2
5
6
7
7
7
8
9
9
10
11
13
15
15
16
17
20
22
22
23
27
30
32
33
35
35
36
36
36
37
37
38
38
39
40
41
43

Modelao Geomtrica
3.1 Coordenadas . . . . . . . . . . . . . . . . . . . .
3.2 Listas . . . . . . . . . . . . . . . . . . . . . . . .
3.3 Operaes com Coordenadas . . . . . . . . . .
3.4 Abstraco de Dados . . . . . . . . . . . . . . .
3.5 Tipos Abstractos . . . . . . . . . . . . . . . . . .
3.6 Coordenadas Bidimensionais . . . . . . . . . .
3.7 Coordenadas em AutoCad . . . . . . . . . . . .
3.8 Coordenadas Polares . . . . . . . . . . . . . . .
3.9 A funo command . . . . . . . . . . . . . . . .
3.10 Variantes de Comandos . . . . . . . . . . . . .
3.11 ngulos em Comandos . . . . . . . . . . . . . .
3.12 Efeitos Secundrios . . . . . . . . . . . . . . . .
3.13 Sequenciao . . . . . . . . . . . . . . . . . . .
3.14 A Ordem Drica . . . . . . . . . . . . . . . . . .
3.15 Parametrizao de Figuras Geomtricas . . . .
3.16 Documentao . . . . . . . . . . . . . . . . . . .
3.17 Depurao . . . . . . . . . . . . . . . . . . . . .
3.17.1 Erros Sintticos . . . . . . . . . . . . . .
3.17.2 Erros Semnticos . . . . . . . . . . . . .
3.18 Modelao Tridimensional . . . . . . . . . . . .
3.18.1 Slidos Tridimensionais Pr-Definidos .
3.18.2 Coordenadas Cilndricas . . . . . . . . .
3.18.3 Coordenadas Esfricas . . . . . . . . . .
3.18.4 Modelao de Colunas Dricas . . . . .
Funes
4.1 Variveis Locais . . . . . . . . . . . .
4.1.1 Declarao . . . . . . . . . . .
4.1.2 Atribuio . . . . . . . . . . .
4.2 Variveis Globais . . . . . . . . . . .
4.3 Variveis Indefinidas . . . . . . . . .
4.4 Propores de Vitrvio . . . . . . . .
4.5 Recurso . . . . . . . . . . . . . . . .
4.6 Recurso em Arquitectura . . . . . .
4.7 Depurao de Programas Recursivos
4.7.1 Trace . . . . . . . . . . . . . .
4.8 Templos Dricos . . . . . . . . . . . .
4.9 A Ordem Jnica . . . . . . . . . . . .
4.10 Recurso na Natureza . . . . . . . .

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

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

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

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

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

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

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

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

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

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

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

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

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

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

44
44
45
46
47
50
52
53
54
56
62
63
65
65
68
72
75
79
79
81
83
83
88
90
92

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

94
94
96
96
97
99
99
106
111
115
116
120
131
138

Atribuio
5.1 Aleatoriedade . . . . . . . . . . . . . . . . .
5.2 Nmeros Aleatrios . . . . . . . . . . . . .
5.3 Estado . . . . . . . . . . . . . . . . . . . . .
5.4 Estado Local e Estado Global . . . . . . . .
5.5 Escolhas Aleatrias . . . . . . . . . . . . . .
5.5.1 Nmeros Aleatrios Fraccionrios .
5.5.2 Nmeros Aleatrios num Intervalo
5.6 Planeamento Urbano . . . . . . . . . . . . .

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.

143
143
144
147
148
149
152
153
157

Listas
6.1 Pares . . . . . . . . . . . . . . . .
6.2 Representao Grfica de Pares .
6.3 Tipos Recursivos . . . . . . . . .
6.4 Recurso em Listas . . . . . . . .
6.5 Predicados sobre Listas . . . . . .
6.6 Enumeraes . . . . . . . . . . . .
6.7 Comparao de Listas . . . . . .
6.8 Comparao de Smbolos . . . .
6.9 Comparao Estrutural de Listas

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

165
167
168
169
170
174
176
178
180
181

Carregamento de Ficheiros
7.1 Carregamento de Dependncias .
7.2 Mdulos . . . . . . . . . . . . . .
7.3 Carregamento Automtico . . . .
7.4 Interpretao e Compilao . . .

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

.
.
.
.

182
183
184
189
189

Listas de Coordenadas
8.1 Polgonos . . . . . . . . . . . . . .
8.1.1 Estrelas Regulares . . . .
8.1.2 Polgonos Regulares . . .
8.2 Iterao em Listas . . . . . . . . .
8.3 Linhas Poligonais e Splines . . . .
8.4 Trelias . . . . . . . . . . . . . . .
8.4.1 Desenho de Trelias . . .
8.4.2 Gerao de Coordenadas
8.4.3 Trelias Espaciais . . . . .

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

.
.
.
.
.
.
.
.
.

191
191
192
196
197
200
204
205
212
216

Manipulao de Entidades
9.1 Listas de Associaes . . .
9.2 A funo assoc . . . . . .
9.3 Modificao de Entidades
9.4 Criao de Entidades . . .
9.5 Listas de Propriedades . .

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

221
223
225
226
226
229

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

.
.
.
.
.

9.6
9.7
9.8
9.9
9.10
9.11
9.12
9.13
9.14
9.15
9.16
9.17
9.18
9.19

Leitura e Modificao de Entidades . . . . .


Eliminao de Entidades . . . . . . . . . . .
ltima Entidade . . . . . . . . . . . . . . . .
Iterao de Entidades . . . . . . . . . . . . .
Entidades Compostas . . . . . . . . . . . . .
Redesenho de Entidades . . . . . . . . . . .
Criao de Linhas Poligonais Leves . . .
Manipulao de Linhas Poligonais Leves
Seleco Interactiva de Entidades . . . . . .
Definio de Comandos AutoCad . . . . . .
Obteno de Informao . . . . . . . . . . .
reas de Polgonos . . . . . . . . . . . . . .
Entidades com Informao Adicional . . .
Escadas . . . . . . . . . . . . . . . . . . . . .
9.19.1 Dimensionamento de Escadas . . .
9.19.2 Dimensionamento de Lanos . . . .
9.19.3 El Castillo . . . . . . . . . . . . . .
9.20 Caixas de Escada . . . . . . . . . . . . . . .

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

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

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

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

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

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

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

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

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

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

236
237
237
238
239
241
241
244
245
246
247
247
257
260
266
276
277
279

Introduo

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. No caso da
transmisso de conhecimentos mais complexos j isso poder ser muito
mais difcil.
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,
ou uma deficiente compreenso do conhecimento transmitido.
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
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.
5

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 interpret-la 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. Embora a maioria dos leitores imagine
correctamente que o autor pretendia a multiplicao dos sucessores dos
nmeros anteriores, leitores haver cuja imaginao delirante poder levlos a 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, o clculo deixa de fazer sentido, o que mostra que, na verdade, a imaginao necessria para a interpretao da frmula no se restringe 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
6

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? 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
da programao consiste, precisamente, na 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.
7

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
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. 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
8

programao empregue o Auto Lisp, um dialecto de uma famosa linguagem de programao denominada Lisp.
Apesar de a linguagem GDL parecer 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
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
apenas 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, o Lisp permite que o desenvolvimento, teste e correco de programas seja feito de uma forma incremen9

tal, o que facilita muito a tarefa do programador.

1.3

Exerccios

Exercicio 1.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.
Exercicio 1.2 O que uma linguagem de programao? Para que serve?

A Linguagem Auto Lisp

Nesta seco vamos descrever a linguagem de programao Lisp que iremos usar ao longo deste texto. Antes, porm, vamos discutir alguns aspectos que so comuns a todas as linguagens.

2.1

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
10

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, 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 que, embora ligeiramente diferente da usual em
Auto Lisp, tem a vantagem de ser visualmente mais compacta.

2.2

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 relembrar 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.
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

2.3

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.
_$

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, o
que 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.
Exercicio 2.1 O que o REPL?

2.4

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.
12

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
representem 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
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. Por agora, vamos debruar-nos sobre os elementos mais bsicos, os
chamados elementos primitivos da linguagem, 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.

2.5

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:
13

_$ 0.1
0.1
_$ .1
; error: misplaced dot on input

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.
Exercicio 2.2 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.

2.6

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 notao infixa, por o operador ocorrer entre os operandos) que, normalmente,
14

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 podemos 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
2

Estes operadores dizem-se varidicos.

15

pelo facto de ser necessrio ler todos os operandos antes de se perceber o


que se vai fazer com eles.

2.7

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. A correcta delimitao dos
elementos da combinao feita apenas pela sua separao visual e pela
utilizao de parnteses correctamente emparelhados.
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:


16

(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.
Exercicio 2.3 Qual a diferena entre a notao prefixa e a notao infixa?
Exercicio 2.4 Em matemtica, usal empregar simultaneamente as diferentes notaes prefixa, infixa e posfixa. Escreva exemplos de expresses matemticas que utilizem estas diferentes notaes.
Exercicio 2.5 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
7. 1 (2 + 3)
8. 2 2 + 3 3 3
Exercicio 2.6 Converta as seguintes expresses da notao prefixa do Lisp para a notao
infixa da aritmtica:
1. (* (/ 1 2) 3)
2. (* 1 (- 2 3))
3. (/ (+ 1 2) 3)
4. (/ (/ 1 2) 3)
5. (/ 1 (/ 2 3))
6. (- (- 1 2) 3)
7. (- 1 2 3)
Exercicio 2.7 Indente a seguinte expresso de modo a ter um nico operando por linha.
(* (+ (/ 3 2) (- (* (/ 5 2) 3) 1) (- 3 2)) 2)

17

2.8

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.
Exercicio 2.8 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)

2.9

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.

18

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


_$ "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.

2.10

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:

19

_$ (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 carcter. 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:
_$ (substr "abcd" 1)
"abcd"
_$ (substr "abcd" 2)
"bcd"
_$ (substr "abcd" 2 2)
"bc"
Exercicio 2.9 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)

2.11

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.
20

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.
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
21

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
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
Em resumo, 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:
22

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.
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.
Exercicio 2.10 Defina a funo dobro que, dado um nmero, calcula o seu dobro.

2.12

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 ;.
3

Dentro do conceito de espao em branco consideram-se os caracteres de espao, de


tabulao e de mudana de linha.

23

Todos os restantes caracteres se podem usar para nomes de funes mas,


na prtica, a criao de nomes segue algumas regras que convm ter presentes:
Apenas se devem usar as letras do alfabeto, os dgitos, os smbolos
aritmticos e alguns caracteres de pontuao, como 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
converte euros para libras poder ter como nome euros->libras
ou, ainda melhor, libras<-euros.
Exercicio 2.11 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.
Exercicio 2.12 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.
Exercicio 2.13 Defina a funo graus<-radianos que recebe uma quantidade angular
em radianos e calcula o valor correspondente em graus.
Exercicio 2.14 Defina a funo que calcula o permetro de uma circunferncia de raio r.
4
Esta letra a abreviatura de predicado, um tipo de funo que iremos estudar mais
frente.

24

2.13

Funes de Mltiplos Parmetros

Todas as funes que definimos at este momento possuem um nico parmetro. Vamos agora considerar 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
bc
A(b, c) =
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:
(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.
Exercicio 2.15 Defina uma funo que calcula o volume de um paralelippedo a partir do
seu comprimento, altura e largura. Empregue nomes suficientemente claros.
Exercicio 2.16 Defina a funo media que calcula o valor mdio entre dois outros valores.
Por exemplo (media 2 3)2.5.

2.14

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:
25

(area-circulo 2)
(* pi (quadrado 2))
(* 3.14159 (quadrado 2))
(* 3.14159 (* 2 2))
(* 3.14159 4)
12.5664
Exercicio 2.17 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.

2.15

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
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, o Lisp providencia um conjunto razoavelmente grande
de funes pr-definidas. Em muitos casos, so suficientes para o que pretendemos, mas 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 escreve-se (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.

26

Funo
+
-

Argumentos
Vrios nmeros
Vrios nmeros

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

*
/
1+
1abs
sin
cos
atan

Um nmero
Um nmero
Dois nmeros
Um nmero
Vrios nmeros
Vrios nmeros
Dois ou mais
nmeros

sqrt
exp
expt
log
max
min
rem

fix
float

Um nmero
Um nmero

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.

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


Exercicio 2.18 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?

27

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
ex
xy
log x
bxc

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


Exercicio 2.19 Embora o seno (sin) e o cosseno (cos) sejam funes pr-definidas em Auto
sin x
Lisp, a tangente (tan) no . Defina-a a partir da frmula tan x = cos
.
x
Exercicio 2.20 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
(acos), cujas definies matemticas so:
x
asin x = atan
1 x2

1 x2
acos x = atan
x
Exercicio 2.21 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.
Exercicio 2.22 Para alm da funo round que arredonda para o inteiro mais prximo e da

28

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.


funo fix que arredonda para baixo, usual considerar ainda a funo ceiling que
arrendonda para cima. Defina esta funo.
Exercicio 2.23 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

Exercicio 2.24 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))
Exercicio 2.25 Defina a funo 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.

29

Exercicio 2.26 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.
Para simplificar a vida destas pessoas, pretende-se que implemente uma funo denominada requerimento que, convenientemente parametrizada, 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.
Exercicio 2.27 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 parametrizada, gera a string adequada para
os trs projectos anteriores, tendo o cuidado de a generalizar para qualquer outra situao
do mesmo gnero.

2.16

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.5 Isto implica que apenas se conseguem representar inteiros desde
5
Em AutoCad, a gama de inteiros ainda mais pequena pois a sua representao s 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.

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.
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.6
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, mas antes a
um crculo em que a seguir ao maior nmero inteiro positivo aparece o menor nmero inteiro negativo. Este comportamento encontra-se 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:
6

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.

31

_$ (* (/ 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
converso automatica para o tipo real.7 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.
Exercicio 2.28 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))
Exercicio 2.29 Explique o seguinte comportamento do Auto Lisp:8
7

Note-se que estamos apenas a falar da leitura de nmeros. Como j vimos, as operaes
aritmticas usuais no apresentam este comportamento.
8
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

Exercicio 2.30 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.
Exercicio 2.31 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.

2.17

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.
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
33

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
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.
34

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 como resultado, apenas
escreve 0.1.
Exercicio 2.32 Traduza a seguinte definio para Auto Lisp:
f (x) = x 0.1 (10 x 10)
Exercicio 2.33 Usando a funo definida na questo anterior, calcule em Auto Lisp o valor
das seguintes expresses e explique os resultados:
(f
(f
(f
(f
(f
(f

5.1)
51367.7)
176498634.7)
1209983553611.9)
19843566622234755.9)
553774558711019983333.9)

Exercicio 2.34 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.

2.18

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 da forma que ilustrmos na seco 2.11, 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 en35

tidade em questo , como vimos, uma funo. O mesmo comportamento


ocorre para qualquer outra funo pr-definida na linguagem:9
_$ +
#<SUBR @0a87c5a8 +>
_$ *
#<SUBR @0a87c588 *>

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.14159

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.

2.19

Smbolos como Valores

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 aos valores. O que mais interessante que os smbolos so, eles
prprios, valores!
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)
+)

Como podemos ver, a funo type devolve-nos o tipo do seu argu9


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.

36

mento: 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 valores 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 valor REAL que foi devolvido pela funo type um smbolo. Note-se que, contrariamente ao
que acontece com o smbolo pi que est associado ao nmero 3.14159 . . . ,
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.14159 . . . 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, recordemos, 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 avalie um determinado smbolo, i.e., queremos que
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)
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.
37

A razo de ser do quote est associada distino que existe entre as


frases Escreve o teu nome e Escreve o teu nome . No primeiro caso a
frase tem de ser completamente interpretada para que o leitor possa dizer
qual o seu prprio nome. No segundo caso, as plicas esto l para indicar
ao leitor que ele no deve interpretar o que est entre plicas e deve limitarse a escrever 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)
Exercicio 2.35 Qual o significado da expresso pi?

2.20

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 do nmero x, equivale ao simtrico do nmero, se este for negativo, ou ao prprio nmero, caso contrrio. Usando a moderna notao da
matemtica, temos:
(
x, se x < 0
|x| =
x,
caso contrrio.
Esta funo ter, portanto, de testar se o seu argumento negativo e
escolher uma de duas alternativas: ou avalia para o prprio argumento, ou
avalia para o seu simtrico.
Estas expresses, cujo valor depende de um ou mais testes a realizar
previamente, so designadas expresses condicionais.

2.21

Expresses Lgicas

Uma expresso condicional assume a forma de se expresso 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 0) testa se o valor de x menor que zero.
Se for, a expresso avalia para verdade, caso contrrio avalia para falso.

38

2.22

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.10 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. Logicamente, se t diferente de nil e se nil o nico valor que representa a falsidade, ento t representa necessariamente a verdade.

2.23

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.
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).

2.24

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:
10

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

39

_$ (> 4 3)
t
_$ (< 4 3)
nil
_$ (<= (+ 2 3) (- 6 1))
t

2.25

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.
Exercicio 2.36 Qual o valor das seguintes expresses?
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)

2.26

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.
40

_$ (< 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:11
_$ (/= 1 2 3)
T
_$ (/= 1 2 1)
T

2.27

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
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

"pois" "pois")

2.28

Reconhecedores

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

Para alm dos operadores relacionais, existem muitos outros predicados


em Lisp, como por exemplo o zerop, que testa se um nmero zero:
11
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.

41

_$ (zerop 1)
nil
_$ (zerop 0)
t

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


adoptada nalguns dialectos de 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. Outros dialectos preferem terminar os nomes dos predicados com um ponto
de interrogao precisamente porque, na prtica, a invocao de um predicado corresponde a uma pergunta que fazemos. Por motivos histricos,
nem todos os predicados pr-definidos no Auto Lisp seguem estas convenes. No entanto, nos predicados que definirmos devemos ter o cuidado
de as seguir.
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.

2.29

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:
_$ (numberp 1)
T
_$ (numberp nil)
nil
_$ (numberp "Dois")
nil

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 2.19, temos:

42

_$ (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 (pr-definidas ou no):
(defun stringp (obj)
(= (type obj) str))
(defun functionp (obj)
(or (= (type obj) subr)
(= (type obj) usubr)))

2.30

Exerccios

Exercicio 2.37 O que uma expresso condicional? O que uma expresso lgica?
Exercicio 2.38 O que um valor lgico? Quais so os valores lgicos empregues em Auto
Lisp?
Exercicio 2.39 O que um predicado? D exemplos de predicados em Auto Lisp.
Exercicio 2.40 O que um operador relacional? D exemplos de operadores relacionais em
Auto Lisp.
Exercicio 2.41 O que um operador lgico? Quais so os operadores lgicos que conhece
em Auto Lisp?
Exercicio 2.42 O que um reconhecedor? O que um reconhecedor universal? D exemplos em Auto Lisp.
Exercicio 2.43 Traduza para Lisp as seguintes expresses matemticas:
1. x < y

43

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

2.31

Seleco

Se observarmos a definio matemtica da funo valor absoluto


(
x, se x < 0
|x| =
x,
caso contrrio.
constatamos que ela emprega uma expresso condicional da forma
(
expresso consequente, se expresso lgica
expresso alternativa, caso contrrio.
que, em linguagem natural, se traduz para se expresso lgica, ento expresso consequente, caso contrrio, expresso alternativa.
A avaliao de uma expresso condicional feita atravs da avaliao
da expresso lgica que, se produzir verdade, implica a avaliao da expresso
consequente e, se produzir falso, implica a avaliao da expresso alternativa.
No caso da linguagem Lisp, a notao usada para descrever expresses condicionais ainda mais simples do que na matemtica pois baseia-se
num simples operador, o if, denominado operador de seleco por permitir
seleccionar entre duas alternativas. A sintaxe do operador if a seguinte:
(if expresso lgica
expresso consequente
expresso alternativa)

O valor de uma expresso condicional que usa o operador if obtido


da seguinte forma:
1. A expresso lgica avaliada.
2. Se o valor obtido da avaliao anterior verdade, o valor da combinao o valor da expresso consequente.
3. Caso contrrio (ou seja, o valor obtido da avaliao anterior falso),
o valor da combinao o valor da expresso alternativa.
Este comportamento, em tudo idntico ao que teramos em Matemtica,
pode ser confirmado pelos seguintes exemplos:
44

_$ (if (> 3 2)
1
2)
1
_$ (if (> 3 4)
1
2)
2

Empregando o if podemos agora definir a funo valor absoluto por


simples traduo da definio matemtica para Auto Lisp:
(defun abs (x)
(if (< x 0)
(- x)
x))

O objectivo fundamental do operador if permitir 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))

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
Em linguagem natural, dizemos que se x for negativo, o valor de sgn x
1, caso contrrio, se x for 0, o valor 0, caso contrrio, o valor 1.
Isto mostra que, na verdade, a definio anterior emprega duas expresses
condicionais encadeadas, da forma:

1
se x < 0

(
sgn x =
0 se x = 0

caso contrrio

1 caso contrrio
Assim sendo, para definirmos esta funo em Lisp, temos de empregar
dois ifs:
45

(defun signum (x)


(if (< x 0)
-1
(if (= x 0)
0
1)))

2.32

Seleco MltiplaA Forma cond

Quando uma expresso condicional necessita de vrios ifs encadeados,


possvel que o cdigo comece a ficar mais difcil de ler. Neste caso, prefervel usar uma outra forma que torne 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)
-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
46

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 . . . .
Exercicio 2.44 Qual o significado de (cond (expr1 expr2))?
Exercicio 2.45 Qual o significado de (cond (expr1) (expr2))?
Exercicio 2.46 Defina uma funo soma-maiores que recebe trs nmeros como argumento e determina a soma dos dois maiores.
Exercicio 2.47 Defina a funo max3 que recebe trs nmeros como argumento e calcula o
maior entre eles.
Exercicio 2.48 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.

Modelao Geomtrica

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. Noutros casos, ser
necessrio introduzirmos novos tipos de dados. Nesta seco iremos estudar o modo de o fazermos e exemplific-lo-emos com um tipo de dados que
nos ser particularmente til para a modelao de entidades geomtricas:
coordenadas.

3.1

Coordenadas

A Arquitectura pressupe a localizao de elementos no espao. Essa localizao expressa-se em termos do que se designa por coordenadas: cada
coordenada um nmero e uma sequncia de coordenadas identifica univocamente um ponto no espao. A Figura 2 esquematiza uma possvel
sequncia de coordenadas (x, y, z) que identificam o ponto P num espao
tridimensional. Diferentes sistemas de coordenadas so possveis e, no caso
da Figura 2, estamos a empregar aquele que se designa por sistema de coordenadas rectangulares, tambm conhecido por sistema de coordenadas Cartesianas, em honra do seu inventor: Ren Descartes.12
Se queremos trabalhar com coordenadas em Auto Lisp, temos de saber
como formar sequncias de nmeros. Vimos, nas seces anteriores, que o
Auto Lisp sabe trabalhar com nmeros. Vamos agora mostrar que tambm
sabe trabalhar com sequncias. Para isso, vamos introduzir o conceito de
lista.
12

Descartes foi um importantssimo filsofo Francs do sculo XVII, autor da famosa


frase Cogito ergo sum (penso, logo existo) e de inmeras contribuies na Matemtica e na
Fsica.

47

Y
P

y
X

Figura 2: Coordenadas Cartesianas de um ponto no espao.

3.2

Listas

Uma lista uma sequncia de elementos. Cada elemento um valor de um


qualquer tipo de dados, como seja um nmero, uma cadeia de caracteres,
ou mesmo uma outra lista. Em Lisp, podemos criar listas usando a funo
pr-definida list. Eis um exemplo da criao de uma lista de nmeros:
_$ (list 1 2 3 4)
(1 2 3 4)

Note-se que a funo list, semelhana de outras funes como a adio e multiplicao, recebe qualquer nmero de argumentos, agrupandoos todos numa lista. Note-se ainda que quando o Lisp escreve a lista resultante, emprega a mesma sintaxe que usada para a escrita de combinaes,
i.e., separando os elementos da lista com um espao e envolvendo-os num
par de parntesis. Existe, contudo, uma excepo a esta regra: uma lista
vazia, i.e., uma lista sem elementos, escrita usando o smbolo nil, exactamente o mesmo que usado para representar a falsidade. O nome nil
uma contraco da palavra nihil que, em latim, significa o vazio. isso que
acontece na seguinte interaco:
_$ (list)
nil

Como se pode ver no exemplo anterior, para construirmos uma lista


sem elementos basta invocar a funo list sem argumentos. Quando necessrio, podemos reconhecer a lista vazia empregando o predicado null.
48

A funo null devolve verdade quando aplicada a uma lista vazia e falso
em qualquer outro caso:
_$ (null (list 1 2 3))
nil
_$ (null (list))
T

Uma vez que a construo de uma lista 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 da lista. Assim, temos:
_$ (list (+ 1 2) (* 3 4))
(3 12)
_$ (list 1 "dois" (+ 1 2))
(1 "dois" 3)
_$ (list (area-circulo 10) (area-triangulo 20 30))
(314.159 300)

A partir do momento em que temos uma lista de entidades, podemos


estar interessados em saber quais so os elementos dessa lista. O Lisp disponibiliza vrias formas de o fazermos. Uma das mais simples empregar
a funo pr-definida nth (abreviatura de inglesa de n-simo) que recebe a
posio do elemento a que queremos aceder e a lista que o contm.13 Para
usarmos correctamente esta funo preciso termos em conta que o primeiro elemento da lista ocupa a posio zero. A seguinte interaco ilustra
este comportamento:
_$ (nth 0 (list (+ 1 2) (* 3 4)))
3
_$ (nth 1 (list 1 "dois"))
"dois"
Exercicio 3.1 Qual o resultado da avaliao da expresso (list (list 1 2) 3)?
Exercicio 3.2 Qual o resultado da avaliao da expresso (nth 0 (list (list 1 2) 3))?
Exercicio 3.3 Qual o resultado da avaliao da expresso (nth 1 (list (list 1 2) 3))?

3.3

Operaes com Coordenadas

A partir do momento em que sabemos construir listas, podemos criar coordenadas e podemos definir operaes sobre essas coordenadas. Para criarmos coordenadas tridimensionais podemos simplesmente juntar numa
lista os trs nmeros das coordenadas (x, y, z). Por exemplo, o ponto do
espao cartesiano (1, 2, 3) pode ser construdo atravs de:
13

Mais frente iremos ver que existem formas mais eficientes de aceder aos elementos de
uma lista. Por agora, e apenas por motivos pedaggicos, vamos limitar-nos funo nth.

49

_$ (list 1 2 3)
(1 2 3)

Uma vez que estamos a fazer uma lista que contm, primeiro, a coordenada x, a seguir, a coordenada y e, por ltimo, a coordenada z, podemos
obter estes valores usando a funo nth com os ndices 0, 1, e 2, respectivamente.
Para melhor percebermos a utilizao destas funes, imaginemos que
pretendemos definir uma operao que mede a distncia d entre os pontos P0 = (x0 , y0 , z0 ) e P1 = (x1 , y1 , z1 ). Essa distncia determinada pela
frmula
p
d = (x1 x0 )2 + (y1 y0 )2 + (z1 z0 )2
e a sua traduo para Auto Lisp ser:
(defun distancia (p0
(sqrt (+ (quadrado
(quadrado
(quadrado

p1)
(- (nth 0 p1) (nth 0 p0)))
(- (nth 1 p1) (nth 1 p0)))
(- (nth 2 p1) (nth 2 p0))))))

Podemos agora experimentar a funo com um caso concreto:


_$ (distancia (list 1 2 3) (list 8 6 4))
8.12404

3.4

Abstraco de Dados

Embora tenhamos pensado na operao distancia 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 list e nth 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 listas.
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 que, tal como as coordenadas, tambm agrupam elementos mais simples. Uma data, por exemplo, define-se como uma sequncia
de nmeros inteiros: o dia, o ms, e o ano. semelhana do que fizemos
com as coordenadas, tambm poderiamos criar datas em Auto Lisp empregando listas. medida que formos implementando em Auto Lisp as
funes que manipulam estes conceitos, a utilizao sistemtica das funes list e nth far com que seja cada vez mais difcil perceber qual ou
quais os tipos de dados a que se destina uma determinada funo. De facto,
a partir apenas das funes list e nth no podemos saber se estamos a
lidar com coordenadas, datas, ou qualquer outro tipo de dados que tenhamos implementado em termos de listas.
50

Para resolvermos este problema necessrio preservarmos no Auto


Lisp o conceito original que pretendemos implementar. Para isso, temos de
abstrair a utilizao que fazemos das listas, escondendo-a no interior de
funes que representem explicitamente os conceitos originais. Para exemplificar esta abordagem vamos reconsiderar o conceito de coordenadas cartesianas e vamos introduzir funes apropriadas para esconder a utilizao
das listas.
Assim, para construirmos as coordenadas (x, y, z) a partir dos seus componentes x, y, e z, ao invs de usarmos directamente a funo list vamos
definir uma nova funo que vamos denominar de xyz:14
(defun xyz (x y z)
(list x y z))

A construo de coordenadas por intermdio da funo xyz apenas o


primeiro passo para abstrairmos a utilizao das listas. O segundo passo
a criao de funes que acedem s componentes x, y, e z, das coordenadas
(x, y, z). Para isso, vamos definir, respectivamente, as funes cx, cy, e
cz, como abreviaturas de, respectivamente, coordenada x, coordenada y, e
coordenada z:
(defun cx (c)
(nth 0 c))
(defun cy (c)
(nth 1 c))
(defun cz (c)
(nth 2 c))

As funes xyz, cx, cy, e cz constituem uma abstraco das coordenadas que nos permitem manipular coordenadas sem termos de pensar
na sua implementao em termos de listas. Esse facto torna-se evidente
quando reescrevemos a funo distancia usando estas novas funes:
(defun distancia (p0
(sqrt (+ (quadrado
(quadrado
(quadrado

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

Reparemos que, contrariamente ao que acontecia com a verso anterior,


a leitura desta funo d agora uma ideia clara do que ela faz. Ao invs de
termos de pensar em termos da posio dos elementos numa lista, vemos
que a funo lida com as coordenadas x, y, e z. A utilizao da funo tambm mostra claramente que o que ela est a manipular so coordenadas:
14

Naturalmente, podemos considerar outro nome igualmente apropriado como, por


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

51

P0
z
P

y
y

X
Figura 3: O ponto P 0 como resultado da translao do ponto P = (x, y, z)
de x no eixo do X, y no eixo do Y , e z no eixo do Z.
_$ (distancia (xyz 1 2 3) (xyz 8 6 4))
8.12404

A introduo das operaes de coordenadas xyz, cx, cy, e cz 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 listas cujo primeiro elemento a coordenada x, cujo
segundo elemento a coordenada y, e cujo terceiro elemento a coordenada z, 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 calcula a posio de um ponto aps
uma translaco, expressa em termos das componentes ortogonais x , y ,
e z , tal como est apresentado na Figura 3. Como se pode ver nesta Figura, sendo P = (x, y, z), ento teremos P 0 = (x + x , y + y , z + z ). Para
simplificar o uso desta funo, vamos denomin-la +xyz. Naturalmente,
ela precisa de receber, como parmetros, o ponto de partida P e os incrementos x , y , e z , que iremos denominar dx, dy, e dz, respectivamente.
A definio da funo fica ento:
(defun +xyz (p dx dy dz)
(xyz (+ (cx p) dx)
(+ (cy p) dy)
(+ (cz p) dz)))

Uma vez que esta funo recebe coordenadas como argumento e produz coordenadas como resultado, ela constitui outra importante adio ao
52

conjunto de operaes disponveis para lidar com coordenadas. Naturalmente, podemos usar a funo +xyz para definir novas funes como, por
exemplo, os casos particulares de deslocamento horizontal e vertical que se
seguem:
(defun +x (p dx)
(+xyz p dx 0 0))
(defun +y (p dy)
(+xyz p 0 dy 0))
(defun +z (p dz)
(+xyz p 0 0 dz))

Igualmente til a definio de funes que operam deslocamentos


em diagonal ao longo dos vrios planos ortogonais XY , XZ, e Y Z:
(defun +xy (p dx dy)
(+xyz p dx dy 0))
(defun +xz (p dx dz)
(+xyz p dx 0 dz))
(defun +yz (p dy dz)
(+xyz p 0 dy dz))

O efeito destas funes visivel na Figura 4.


Exercicio 3.4 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.
Exercicio 3.5 Defina a funo =c que compara as coordenadas de dois pontos e devolve
verdade apenas quando so coincidentes. Note que dois pontos so coincidentes quando
as coordenadas x, y e z dos dois pontos forem iguais.

3.5

Tipos Abstractos

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


tipo de dadosas coordenadas cartesianas tridimensionaise, mais importante, permite-nos abstrair a implementao desse tipo de dados, i.e.,
esquecer o modo como est implementado. Por este motivo, denomina-se
este novo tipo de dados por tipo abstracto. abstracto porque apenas existe
no nosso pensamento. Para o Lisp, como vimos, as coordenadas so apenas listas de nmeros. Essa particular combinao de dados denomina-se
representao dos elementos do tipo.
Um tipo abstracto caracterizado 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 abs53

+yz
Y

z
+z

+xyz

+y
z

y
+xz

+xy
x
X

z
y
+x

Figura 4: As translaces realizadas pelas funes +x, +y, +z, +xy, +xz,
+yz, e +xyz a partir de um ponto arbitrrio P e dos deslocamentos x ,
y , e z .
tracto, 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 tridimensionais, 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
Estas equaes asseguram que, 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.
Exercicio 3.6 O que um construtor de um tipo abstracto?
Exercicio 3.7 O que um selector de um tipo abstracto?
Exercicio 3.8 O que a representao de um tipo abstracto?

54

3.6

Coordenadas Bidimensionais

Tal como as coordenadas tridimensionais localizam pontos no espao, as


coordenadas bidimensionais localizam pontos no plano. A questo que se
coloca : qual plano? Do ponto de vista matemtico, a questo no relevante pois perfeitamente possvel pensar em geometria no plano sem
pensar no plano propriamente dito, mas quando tentamos visualizar essa
geometria no AutoCad, que um programa de modelao tridimensional,
inevitavelmente temos de pensar na localizao desse plano. Se nada for
dito em contrrio, o AutoCad, como alis todos os outros programas de
CAD tridimensionais, considera que o plano bidimensional corresponde
ao plano XY , localizado na cota Z igual a zero. Assim sendo, razovel
assumirmos o mesmo e, consequentemente, vamos considerar a coordenada bidimensional (x, y) como uma notao simplificada da coordenada
tridimensional (x, y, 0).
Usando esta simplificao, podemos definir um construtor de coordenadas bidimensionais custa do construtor de coordenadas tridimensionais:
(defun xy (x y)
(xyz x y 0))

Uma das vantagens da definio de coordenadas bidimensionais como


um caso particular das coordenadas tridimensionais o facto de os selectores cx, cy, e cz serem automaticamente aplicveis a coordenadas bidimensionais e, por arrasto, tambm o so as operaes de coordenadas que
definimos anteriormente, como a distancia e as translaes +x, +y, +z,
+xy, +xz, +yz, e +xyz.
Exercicio 3.9 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 .
Exercicio 3.10 Sabendo que o espelho mximo admissvel para um degrau de 0.18m,
defina uma funo que calcula o nmero mnimo de espelhos que a escada representada no
seguinte esquema necessita para fazer a ligao entre os pontos P0 e P1 .

55

P1

...

P0

3.7

Coordenadas em AutoCad

Como mostrmos nas seces anteriores, a representao de coordenadas


(x, y, z) que adoptmos baseia-se na utilizao de listas contendo os valores de x, y, e z dispostos por ordem. Para alm de esta representao ser
simples, tem ainda outra enorme vantagem: totalmente compatvel com
a forma como o AutoCad espera receber coordenadas.
Seria ento de esperar que j existissem pr-definidas em Auto Lisp
as operaes que lidam com coordenadas neste formato mas, na verdade,
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.
Na verdade, se fizermos uma anlise dos programas Auto Lisp existentes,
incluindo programas desenvolvidos recentemente, iremos constatar que a
prtica usual a manipulao directa da representao, saltando por cima
de qualquer definio de tipos abstractos. Em particular, a manipulao de
coordenadas geralmente feita usando directamente as operaes do tipo
lista.
Embora sejamos grandes defensores do respeito pela 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 o construtor xyz e os selectores cx, cy,
e cz e vamos evitar aceder directamente representao das coordenadas,
i.e., vamos evitar usar as operaes de listas 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
56

x
Figura 5: Coordenadas rectangulares e polares.
conseguir perceber se uma dada lista presente num programa significa coordenadas ou se significa outro tipo abstracto qualquer.

3.8

Coordenadas Polares

Apesar do AutoCad exigir que as coordenadas estejam descritas no sistema Cartesiano, nada nos impede de usar outros sistemas de coordenadas, desde que tenhamos o cuidado de, antes da passagem de coordenadas
para o AutoCad, proceder sua converso para o sistema Cartesiano.
No caso bidimensional, um dos sistemas mais teis o de coordenadas
polares.
Tal como representado da Figura 5, uma posio no plano bidimensional descrita, em coordenadas rectangulares, pelos nmeros x e y
significando, 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 converter de coordenadas polares para coordenadas rectangulares:
(
x = cos
y = sin
ou de coordenadas rectangulares para coordenadas polares:

p
= x2 + y 2
= arctan y
x
Com base nas equaes anteriores, podemos definir o construtor de coordenadas polares pol (abreviatura de polar) que contri coordenadas
a partir da sua representao polar simplesmente convertendo-a para a representao rectangular equivalente.

57

(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 os selectores de coordenadas rectangulares, i.e., cx e cy:
(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:15


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

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 6. 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 p
(* ro (cos fi))
(* ro (sin fi))))

Como exemplos de utilizao, temos:


_$ (+pol
(2.0 3.0
_$ (+pol
(2.0 2.0
_$ (+pol
(1.0 3.0

(xy 1 2) (sqrt 2) (/ pi 4))


0)
(xy 1 2) 1 0)
0)
(xy 1 2) 1 (/ pi 2))
0)

15

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. Note-se tambm que, como estamos a usar coordenadas bidimensionais, a
coordenada z sempre igual a zero.

58

P0

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


Exercicio 3.11 A funo =c definida no exerccio 3.5 compara as coordenadas de dois pontos e devolve verdade apenas quando so coincidentes. No entanto, se tivermos em conta
que as operaes numricas podem produzir erros de arredondamento, perfeitamente
possvel que duas coordenadas que, em teoria, deveriam ser iguais, na prtica, sejam consideradas diferentes. Por exemplo, o ponto (1, 0) em coordenadas rectangulares pode ser
expresso atravs das coordenadas polares = 1, = mas o Auto Lisp no as considera
iguais, tal como perfeitamente visvel na seguinte interao:
_$ (=c (xy -1 0) (pol 1 pi))
nil
_$ (xy -1 0)
(-1 0 0)
_$ (pol 1 pi)
(-1.0 1.22465e-016 0)

Como se pode ver, embora as coordenadas no sejam iguais, elas so bastante prximas, ou seja, a distncia entre elas muito prxima de zero. Proponha uma nova definio
para a funo =c baseada no conceito de distncia entre as coordenadas.

3.9

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.
Existem diferentes formas 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.16 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.
16

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.

59

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
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 3.7, 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 7.
Finalmente, a funo command pode ser usada para alterar os parmetros de funcionamento do AutoCad. Por exemplo, para desligar os Object
Snaps podemos fazer:
60

AutoCAD
Figura 7: Um crculo com o texto "AutoCad".
_$ (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.
Como exemplo da utilizao de comandos em Auto Lisp, a Figura 8
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")

Exercicio 3.12 Refaa o desenho apresentado na Figura 8 mas utilizando apenas coordenadas rectangulares.

Existem algumas particularidades 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 o comando
erase do AutoCad, o que podemos fazer escrevendo as letras e-r-a-s-e e

61

Raio: 1

Raio: 2

Raio: 4

Figura 8: Crculos e textos especificados usando coordenadas polares.


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:
(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:
62

(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. Assim, 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
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
63

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")

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.
Exercicio 3.13 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:

64

Escreva uma sequncia de expresses que, quando avaliadas, produzem a figura anterior.
Exercicio 3.14 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.
Exercicio 3.15 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.

3.10

Variantes de Comandos

Para a correcta invocao dos comandos do AutoCad a partir do Auto Lisp


necessrio ter em conta que o AutoCad permite ao utilizador, quer a redefinio de comandos, quer a sua eliminao. Isto implica que quando invocamos um comando como, por exemplo, circle, estamos na realidade
a invocar a mais recente redefinio que foi feita para esse comando. S no
caso de no ter sido feita qualquer redefinio do comando e de este no
ter sido eliminado que iremos invocar 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. Assim, perfeitamente possvel que um programa
Auto Lisp que funcionava correctamente num determinado AutoCad, no
consiga funcionar noutro porque, nesse, um ou mais dos comandos usados
pelo programa foram redefinidos ou eliminados.
Para evitar este problema, o AutoCad disponibiliza uma forma de aceder definio original de um comando, independentemente de ele ter
65

sido redefinido ou eliminado: precedendo o nome de um comando com


um ponto ., o AutoCad executa o comando originalmente definido com
esse nome.
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.
Exercicio 3.16 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.

3.11

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
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

66

Figura 9: Tringulos, quadrados e pentgonos sobrepostos com diferentes


ngulos de rotao.
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 9:
(command "_.polygon" 3
(xy 0 0) "_Inscribed" (raio&angulo 1 0))
(command "_.polygon" 3
(xy 0 0) "_Inscribed" (raio&angulo 1 (/ pi 3)))
(command "_.polygon" 4
(xy 3 0) "_Inscribed" (raio&angulo 1 0))
(command "_.polygon" 4
(xy 3 0) "_Inscribed" (raio&angulo 1 (/ pi 4)))
(command "_.polygon" 5
(xy 6 0) "_Inscribed" (raio&angulo 1 0))
(command "_.polygon" 5
(xy 6 0) "_Inscribed" (raio&angulo 1 (/ pi 5)))

67

3.12

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, 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).
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 discutir a sequenciao de efeitos secundrios.

3.13

Sequenciao

At agora, temos combinado expresses matemticas usando operadores


matemticos. Por exemplo, a partir das expresses (sin x) e (cos x),
podemos calcular a sua diviso atravs de (/ (sin x) (cos x)). Isto
possvel porque a avaliao das subexpresses (sin x) e (cos x) ir
produzir dois valores que podem ento ser usados para fazer a diviso.
J no caso de expresses envolvendo a funo command, a sua combinao tem de ser realizada de forma diferente pois, como vimos, a sua
avaliao no produz valores relevantes mas apenas efeitos secundrios.
Como precisamente a realizao desses efeitos que nos interessa, o Auto
Lisp providencia uma forma de os realizarmos sequencialmente, i.e., uns a
seguir aos outros. Essa forma denomina-se progn e serve para fazermos
a sequenciao de efeitos secundrios. A ttulo de exemplo, consideremos
uma funo que desenha um crculo de raio unitrio com um quadrado inscrito ou circunscrito, dependendo de uma escolha do utilizador, tal como

68

Figura 10: Um crculo com um quadrado inscrito ( esquerda) e circunscrito


( direita).
apresentamos na Figura 10.
A definio desta funo poder comear por algo do gnero:
(defun circulo-quadrado (inscrito?)
...)

O desenho produzido pela funo depende, naturalmente, do valor lgico do parmetro inscrito?, ou seja, a definio da funo dever ser
algo da forma:
(defun circulo-quadrado (inscrito?)
(if inscrito?
desenha um crculo e um rectngulo inscrito no crculo
desenha um crculo e um rectngulo circunscrito ao crculo))

O problema que se coloca agora que, em cada caso do if, a funo


necessita de realizar dois efeitos secundrios, nomeadamente, desenhar um
crculo e desenhar um rectngulo. Ora um if apenas admite, em cada caso,
uma nica expresso, o que nos leva a perguntar como que podemos combinar os dois efeitos secundrios numa s expresso. Para este fim, o Auto
Lisp disponibiliza a forma progn. Este operador recebe vrias expresses
que avalia sequencialmente, i.e., 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 relevantes pelos efeitos secundrios que possam
provocar.
Usando o operador progn j podemos detalhar um pouco mais a funo:

69

(defun circulo-quadrado (inscrito?)


(if inscrito?
(progn
desenha um crculo
desenha um rectngulo inscrito no crculo)
(progn
desenha um crculo
desenha um rectngulo circunscrito ao crculo)))

Basta-nos agora traduzir cada um dos desenhos elementares em invocaes da funo command. No caso do rectngulo inscrito, vamos utilizar
coordenadas polares pois sabemos que os vrtices tero de ficar sobre o
crculo, um a /4 e ou outro a + /4 = 5/4. Assim, temos:
(defun circulo-quadrado (inscrito?)
(if inscrito?
(progn
(command "_.circle" (xy 0 0) 1)
(command "_.rectangle" (pol 1 (/ (* 5 pi) 4)) (pol 1 (/ pi 4))))
(progn
(command "_.circle" (xy 0 0) 1)
(command "_.rectangle" (xy -1 -1) (xy 1 1)))))

Reparemos que, na ptica do operador if, quer o consequente, quer a


alternativa, so uma nica expresso, embora cada uma dessas expresses
corresponda sequenciao de duas expresses mais elementares. O operador progn pode assim ser visto como um mecanismo de agrupamento
de expresses.
Apesar de a sequenciao de expresses implicar a utilizao do operador progn, normalmente possvel simplificar essas expresses de forma
a minimizar o seu uso. Por exemplo, uma observao atenta da funo
circulo-quadrado mostra que a criao do crculo sempre feita, independentemente do rectngulo ser inscrito ou circunscrito. Isto permite-nos
reescrever a definio da funo de modo a que a criao do crculo seja
realizada fora do if:
(defun circulo-quadrado (inscrito?)
(progn
(command "_.circle" (xy 0 0) 1)
(if inscrito?
(progn
(command "_.rectangle" (pol 1 (/ (* 5 pi) 4)) (pol 1 (/ pi 4))))
(progn
(command "_.rectangle" (xy -1 -1) (xy 1 1))))))

claro que, agora, os dois progns dentro do if apenas tm uma expresso, pelo que o seu uso perfeitamente dispensvel. Assim, podemos
simplificar a definio para:

70

(defun circulo-quadrado (inscrito?)


(progn
(command "_.circle" (xy 0 0) 1)
(if inscrito?
(command "_.rectangle" (pol 1 (/ (* 5 pi) 4)) (pol 1 (/ pi 4)))
(command "_.rectangle" (xy -1 -1) (xy 1 1)))))

Embora o operador progn seja necessrio sempre que pretendemos


avaliar mais do que uma expresso, existem alguns locais onde o Auto
Lisp assume um progn implcito como, por exemplo, no corpo de uma funo. De facto, quando invocamos uma funo, todas as expresses que
colocamos no seu corpo so avaliadas sequencialmente, sendo o valor da
funo determinado apenas pela ltima das expresses. Esta caracterstica
permite-nos simplificar ainda mais a funo circulo-quadrado, ficando
apenas:
(defun circulo-quadrado (inscrito?)
(command "_.circle" (xy 0 0) 1)
(if inscrito?
(command "_.rectangle" (pol 1 (/ (* 5 pi) 4)) (pol 1 (/ pi 4)))
(command "_.rectangle" (xy -1 -1) (xy 1 1))))
Exercicio 3.17 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 8, coloca o texto a descrever o raio do crculo direita
do crculo. O texto dever ter um tamanho proporcional ao raio do crculo.
Exercicio 3.18 Utilize a funo circulo-e-raio definida na pergunta anterior para reconstituir a imagem apresentada na Figura 8.

3.14

A Ordem Drica

Na Figura 11 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.17 Quando os Romanos copiaram a Ordem Drica introduziramlhe um conjunto de alteraes, em particular, nas caneluras que, muitas
vezes, so simplesmente eliminadas.
17

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.

71

Figura 11: 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.
Embora o nfase desta obra seja na modelao tridimensional, por motivos pedaggicos vamos comear por esquematizar o alado de uma coluna Drica (sem caneluras). Nas seces seguintes iremos estender este
processo para a criao de um modelo tridimensional.
Do mesmo modo que uma coluna Drica se pode decompor nos seus
componentes fundamentaiso fuste, o coxim e o bacotambm 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 12 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
72

(1, 11)

(1, 10.5)

(1, 11)
(1, 10.5)

(0.8, 10)

(0.8, 10)

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

Figura 12: Uma coluna Drica de referncia.


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
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.18
Para completar a figura, ainda necessrio definir uma funo para o
coxim e outra para o baco. No caso do coxim, o raciocnio semelhante:
18

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

73

Figura 13: Uma coluna drica desenhada pelo AutoCad.


(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:
(defun coluna ()
(fuste)
(coxim)
(abaco))

Repare-se, na funo coluna, que ela invoca sequencialmente as funes fuste, coxim e, finalmente, abaco.
A Figura 13 mostra o resultado da invocao da funo coluna.
74

rt

P1 = (x rt , y + a)

P2 = (x + rt , y + a)

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

rb

P3 = (x + rb , y)

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

3.15

Parametrizao 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 parametrizvel, i.e., se a criao
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 parametrizao destas funes vamos comear
por considerar o fuste representado esquematicamente na Figura 14.
O primeiro passo para parametrizarmos 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
P4 , pontos esses que conseguimos calcular facilmente a partir do ponto P .
Desta forma, estamos em condies de definir a funo que desenha
75

rt

P2 = (x rt , y + a)

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

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

rb

P4 = (x + rb , y)

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


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

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


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 15.
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 16.
76

la
rbc

aa
ac

af

rbf

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


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 17 demonstra, o raio do topo do fuste 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 17. Usando os nomes p, a-fuste, r-base-fuste,
77

Figura 18: Variaes de colunas dricas.


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 18.
(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.

3.16

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
78

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 pontoe-vrgula ;, o 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 colunas dricas fica com o aspecto ilustrado na Figura 19. Note-se que o
exemplo destina-se a mostrar as diferentes formas de documentao usadas em Lisp e no a mostrar um exemplo tpico de programa documentado.19
Quando um programa est documentado, a sua leitura permite ficar
com uma ideia muito mais clara do que cada funo faz sem obrigar ao estudo do corpo das funes. Como se v pelo exemplo, 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.
19
Na verdade, o programa to simples que no deveria necessitar de tanta
documentao.

79

;;;;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
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))

Figura 19: Um programa Auto Lisp documentado.


80

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.
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.
Exercicio 3.19 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.
Exercicio 3.20 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 .


Exercicio 3.21 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 .
Exercicio 3.22 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

81

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
(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:

sala i.s.
Area:20.00

quarto

Area:12.00

Area:18.00

3.17

cozinha

despensa

Area:12.00

Area:6.00

quarto
Area:15.00

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.
3.17.1

Erros Sintticos

Os erros sintticos ocorrem quando escrevemos frases que no obedecem


gramtica da linguagem. Como exemplo prtico, imaginemos que pretendamos definir uma funo que criava uma nica coluna drica, que iremos
82

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:20
; 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 2.11, 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. Nessa janela, se clicarmos duas vezes sobre uma
mensagem de erro, somos conduzidos ao local do nosso programa onde o
erro ocorre.
20
Nem todos os dialectos e interpretadores de Lisp possuem exactamente este comportamento mas, variaes parte, os conceitos so os mesmos.

83

3.17.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:21
(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

Nesta 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.
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 mostrar a cascata
21

Consegue detect-lo?

84

de invocaes que acabou por provocar o erro.22 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) -0.3 9)
(FUSTE (nil 0 0) 9 0.5 0.3)
(COLUNA (nil 0 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.23 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 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.
(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
22

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.
23

85

argumentos, cria uma lista cujo primeiro elemento nil e cujos restantes
elementos so 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
quanto mais experincia tivermos na deteco de erros, mais rapidamente
se detectam novos erros.

3.18

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.
3.18.1

Slidos Tridimensionais Pr-Definidos

As verses mais recentes do AutoCad disponibilizam um conjunto de operaes pr-definidas que constroem um slido a partir 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.
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.24 A Figura 20 mostra um conjunto de slidos construdos pela
avaliao das seguintes expresses:25
24

Para se conhecer as especificidades de cada comando recomenda-se a consulta da documentao que acompanha o AutoCad.
25
A construo de uma pirmide exige a especificao simultnea do raio e ngulo da

86

Figura 20: Slidos primitivos em AutoCad.


(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,
cada uma empregando diferentes parmetros, podemos considerar que os
base. Tal como explicado na seco 3.11, a funo raio&angulo constri essa especificao
a partir dos valores do raio e do ngulo.

87

Figura 21: Uma cruz papal.


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
e o seu eixo est posicionado a uma altura igual a 9, 13 e 17 raios. Estas
propores so implementadas pela seguinte funo:

88

(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))))

Um outro exemplo que vale a pena simplificar o do tronco de cone. Do


ponto de vista geomtrico, possvel caracterizarmos um tronco de cone
apenas a partir dos pontos correspondentes aos centros da base e do topo e
dos raios dessa base e desse topo. Assim, podemos definir:
(defun tronco-cone (centro-base raio-base centro-topo raio-topo)
(command "_.cone" centro-base raio-base
"_Top" raio-topo
"_Axis" centro-topo))

A ttulo de exemplo, consideremos a cruzeta representada na Figura 22,


constituida por seis troncos de cone idnticos, com a base centrada num
mesmo ponto e com os topos posicionados ao longo dos eixos coordenados.
Para a modelao deste slido vamos parametrizar o posicionamento
p da base dos troncos de cone, bem como as dimenses de cada tronco de
cone em termos dos raios da base rb e do topo rt e ainda do comprimento
c. Assim, temos:
(defun cruzeta
(tronco-cone
(tronco-cone
(tronco-cone
(tronco-cone
(tronco-cone
(tronco-cone

(p rb rt
p rb (+x
p rb (+y
p rb (+z
p rb (+x
p rb (+y
p rb (+z

c)
p c)
p c)
p c)
p (p (p (-

rt)
rt)
rt)
c)) rt)
c)) rt)
c)) rt))

O caso do cone semelhante ao do tronco de cone, mas em que evitamos


especificar o raio do topo, i.e.,:
(defun cone (centro-base raio-base centro-topo)
(command "_.cone" centro-base raio-base "_Axis" centro-topo))

Em alternativa, se considerar o cone como um caso particular do tronco


de cone em que o raio do topo zero, podemos tambm definir:

89

Figura 22: Uma sobreposio ortogonal de troncos de cone.


(defun cone (centro-base raio-base centro-topo)
(tronco-cone centro-topo raio-base centro-topo 0))

O caso da esfera ainda mais simples:


(defun esfera (centro raio)
(command "_.sphere" centro raio))
Exercicio 3.23 Empregando a funo cruzeta responsvel pela criao do modelo apresentado na Figura 22, determine valores aproximados para os parmetros de modo a conseguir reproduzir os seguintes modelos:

90

Exercicio 3.24 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:

3.18.2

Coordenadas Cilndricas

Vimos nas seces anteriores alguns exemplos da utilizao dos sistemas de


coordenadas rectangulares e polares. Ficou tambm claro que uma escolha
judiciosa do sistema de coordenadas pode simplificar bastante a soluo de
um problema geomtrico.
Para a modelao tridimensional, para alm das coordenadas rectangulares e polares, ainda usual empregarem-se dois outros sistemas de
coordenadas que iremos ver de seguida: as coordenadas cilndricas e as coordenadas esfricas.
Tal como podemos verificar na Figura 23, 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
91

z
y

x
Figura 23: Coordenadas cilndricas.
e o ngulo correspondem s coordenadas polares da projeco do ponto no
plano z = 0.
A partir da Figura 23 fcil vermos que, dado um ponto (, , z) em
coordenadas cilndricas, o mesmo ponto em coordenadas rectangulares
( cos , sin , z)
De igual modo, dado um ponto (x, y, z) em coordenadas rectangulares,
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 rectangulares
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 deslocamento em coordenadas cilndricas, podemos tirar partido da funo +xyz
e definir:
(defun +cil (p ro fi z)
(+xyz p
(* ro (cos fi))
(* ro (sin fi))
z))

92

Exercicio 3.25 Defina os selectores cil-ro, cil-fi e cil-z que devolvem, respectivamente, os componentes , e z de um ponto construdo pelo construtor cil.
Exercicio 3.26 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))

3.18.3

Coordenadas Esfricas

Tal como podemos ver na Figura 24, 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
colatitude26 , znite ou ngulo polar) que o vector faz com o eixo z.
26

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


o plo ( 2 ) e a latitude.

93

P

y

Figura 24: Coordenadas Esfricas


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 deslocamento
em coordenadas esfricas:
(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)


(+xyz p
(* ro (sin psi) (cos fi))
(* ro (sin psi) (sin fi))
(* ro (cos psi))))
Exercicio 3.27 Defina os selectores esf-ro, esf-fi e esf-psi que devolvem, respectivamente, os componentes , e de um ponto construdo pelo construtor esf.

94

Exercicio 3.28 O corte de cabelo de estilo Moicano foi muito utilizado no perodo punk. Ele
consiste em fixar os cabelos em bicos que se dispoem em forma de leque ou crista, tal como
se esquematiza na seguinte imagem.

Defina a funo moicano, de parmetros P , r, c, e , que constri 9 cones de


comprimento c e raio da base r, todos com a base centrada num mesmo ponto P e com os
eixos inclinados uns em relao aos outros por um ngulo , e dispostos ao longo de um
plano que faz um ngulo com o plano XZ.
A ttulo de exemplo, considere que a figura anterior foi produzida pelas seguintes invocaes:
(moicano (xyz 0 0 0) 1.0 10 (/ pi 2) (/ pi 6))
(moicano (xyz 0 15 0) 0.5 15 (/ pi 3) (/ pi 9))
(moicano (xyz 0 30 0) 1.5 6 (/ pi 4) (/ pi 8))

3.18.4

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 3.14. 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 XY ,
com as colunas a crescer ao longo do eixo Y . Agora, ser apenas a
base das colunas que ficar assente no plano XY : o corpo das colunas ir

95

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
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, de seguida, 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)))
Exercicio 3.29 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))

Com estas redefinies, podemos agora repetir as colunas que desenhmos na seco 3.15 e que apresentmos na Figura 18, mas, agora, gerando
96

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


uma imagem tridimensional dessas mesmas colunas, tal como apresentamos na Figura 25:
(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)

Funes

As funes so um dos componentes mais importantes do Auto Lisp e so


a ferramenta fundamental para implementarmos a soluo dos nossos problemas arquitecturais. Nesta seco iremos discutir algumas caractersticas
adicionais das funes.

4.1

Variveis Locais

Consideremos o seguinte tringulo

97

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:27
A=

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

em que s o semi-permetro do tringulo:


s=

a+b+c
2

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
27

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.

98

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
no contexto de uma dada 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.
4.1.1

Declarao

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 2.11) de modo a incorporar a declarao de variveis locais:
(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 fossem parmetros).
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)

4.1.2

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 :

99

(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).
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.

4.2

Variveis Globais

Qualquer referncia a um nome que no est declarado localmente implica


que esse nome seja tratado como uma varivel global.28 O nome pi, por
exemplo, representa a varivel = 3.14159 . . . e pode ser usado em qualquer ponto dos nossos programas. Por esse motivo, o nome pi designa
uma varivel global.
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 ouro29

1+ 5
=
1.6180339887
2
28

Na realidade, o Auto Lisp permite ainda uma terceira categoria de nomes que no so
nem locais, nem globais. Mais frente discutiremos esse caso.
29
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 in-

100

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))

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.
troduzida 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 .

101

4.3

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.30 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:
_$ pi
3.14159
_$ razao-de-ouro
1.61803
_$ (list pi razao-de-oiro)
(3.14159 nil)

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 agrupar o pi e a razao-de-oiro
est, na realidade, a agrupar 3.14159 com nil.
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.

4.4

Propores de Vitrvio

A modelao de colunas dricas que desenvolvemos na seco 3.18.4 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 26 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
30

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

102

Figura 26: Perspectiva da modelao tri-dimensional de colunas cujos parmetros foram escolhidos aleatoriamente. Apenas uma das colunas obedece
aos cnones da Ordem Drica.
si segundo um conjunto de propores bem definidas. Vitrvio,31 no seu
famoso tratado de arquitectura, considera que essas propores derivam
das propores do prprio ser humano:
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 que 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,
1
o 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.
31

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.

103

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, 23 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
/ 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). A Figura 27
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)

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 27.
104

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


Na verdade, a caracterizao da Ordem Drica que apresentmos encontra-se incompleta pois, acerca das propores das colunas, Vitrvio afirmou ainda 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.32
Consideremos ento a definio de uma funo, que iremos denominar
de raio-topo-fuste, que recebe como parmetros a largura da base da
32
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.

105

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))

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.
106

6
7

20
15

5 12
6 12

5
6

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 12 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 um mecanismo 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:
1
6 + b a20
10 c 2
r=
1
7 + b a20
10 c 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
6+136/2
74
7+136/2 = 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
10 + b 10
c
10 c
=
=
a
a
a20
14 + b 10 c 2
12 + b 10 c
14 + b 10 c

107

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 28:33
(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)

Exercicio 4.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.
33

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

108

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

4.5

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:
109

(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
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 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
110

de quatro) se reduz, em ltima anlise, a (* (* (potencia 4 0) 4) 4).


Para que esta expresso seja equivalente a (* 4 4) necessrio que a avaliao de (potencia 4 0) produza 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 o mecanismo 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 surpreendentemente simples.
Existem inmeros exemplos de funes recursivas. Uma das mais simples a funo factorial que se define matematicamente como:
(
1,
se n = 0
n! =
n (n 1)!, caso contrrio.
A traduo desta frmula para Lisp directa:
111

(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.
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:
112

(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.
Exercicio 4.2 A funo de Ackermann definida para nmeros no negativos da seguinte
forma:

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.
Exercicio 4.3 Indique o valor de
1. (ackermann 0 8)
2. (ackermann 1 8)
3. (ackermann 2 8)
4. (ackermann 3 8)
5. (ackermann 4 8)

113

e
c
P
Figura 29: Perfil de uma escada comn n degraus cujo primeiro degrau comea no ponto P e cujos degraus possuem um cobertor c e um espelho
e.

4.6

Recurso em Arquitectura

Como iremos ver, tambm em arquitectura a recurso um conceito fundamental. A ttulo de exemplo, consideremos o perfil de uma escada, tal como
esquematizado na Figura 29 e supunhamos que pretendemos definir uma
funo denominada escada que, dado o ponto P , dados o comprimento
do cobertor c e o comprimento do espelho e de cada degrau e, finalmente,
dado o nmero n de degraus, desenha a escada em AutoCad com o primeiro espelho a comear a partir do ponto P . Dados estes parmetros, a
definio da funo dever comear por:
(defun escada (p c e n)
...)

Para implementarmos esta funo temos de ser capazes de decompor o


problema em subproblemas menos complexos e aqui que a recurso d
uma enorme ajuda: ela permite-nos decompor o desenho de uma escada
com n degraus no desenho de um degrau seguido do desenho de uma escada com n 1 degraus, tal como se apresenta no esquema da Figura 30.
Isto quer dizer que a funo ficar algo da forma:
(defun escada (p c e n)
...
(degrau p c e)
(escada (+xy p c e) c e (- n 1)))

Para desenharmos um degrau, podemos definir a seguinte funo que


cria os segmentos do espelho e do cobertor:
(defun degrau (p c e)
(command "_.pline" p (+y p e) (+xy p c e) ""))

O problema que se coloca agora que a funo escada precisa de parar de desenhar degraus num determinado momento. fcil vermos que
114

n 1 degraus

P0

n degraus

Figura 30: Decomposio do desenho de uma escada de n degraus no desenho de um degrau seguido do desenho de uma escada de n 1 degraus.
esse momento chega quando, ao reduzirmos sucessivamente o nmeros de
degraus, atingimos um ponto em que esse nmero zero. Assim, quando
pedimos para desenhar uma escada com zero degraus, a funo escada
j no precisa de fazer nada. Isso quer dizer que a funo tem de ter a
seguinte forma:
(defun escada (p c e n)
(if (= n 0)
...
...
(degrau p c e)
(escada (+xy p c e) c e (- n 1)))
(defun escada (p0 degraus espelho cobertor / p1 p2)
(if (= degraus 0)
nil
(progn
(setq p1 (+y p0 espelho)
p2 (+x p1 cobertor))
(command "_.pline" p0 p1 p2 "")
(escada p2 (1- degraus) espelho cobertor))))
Exercicio 4.4 Defina uma funo equilibrio-circulos capaz de criar qualquer uma
das figuras apresentadas em seguida:

115

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 .
Exercicio 4.5 Considere o desenho de crculos tal como apresentado na seguinte imagem:
y

r1
r0

Escreva uma funo denominada circulos-radiais 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-radiais (xy 0 0) 10 10 2 0 (/ pi 5))
(circulos-radiais (xy 25 0) 20 10 2 0 (/ pi 10))
(circulos-radiais (xy 50 0) 40 10 2 0 (/ pi 20))

cuja avaliao dever gerar a imagem seguinte:

116

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

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:

117

4.7

Depurao de Programas Recursivos

Vimos, na seco 3.17, 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:
(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.34 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.
34

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.

118

4.7.1 Trace
Vimos, na seco3.17, que o Visual Lisp disponibiliza vrios mecanismos
para fazer a deteco de erros. Um dos mais simples a forma trace
que permite visualizar as invocaes das funes. A forma trace recebe o
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,35 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, encontraremos:


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:
35

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


Auto Lisp.

119

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:

120

...
[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)
...
[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.
Exercicio 4.7 Faa o trace da funo potncia. Qual o trace resultante da avaliao (potencia 2 10)?
Exercicio 4.8 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

121

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.
Exercicio 4.9 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:
a
P
c
Exercicio 4.10 Defina uma funo losangulos capaz de criar a figura apresentada em
seguida:

Note que os losangos possuem dimenses que esto em progresso geomtrica de razo 12 . Dito de outra forma, os losangos mais pequenos tm metade do tamanho do losango
maior em cujas extremidades esto centrados. Os losangos 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 losango maior.
Exercicio 4.11 Considere a escada esquematizada na seguinte figura e destinada a vencer
uma rampa de inclinao .

P
Defina a funo escada-rampa que recebe o ponto P , o ngulo , o cobertor c e o
nmero de degraus n e constri a escada descrita no esquema anterior.
Exercicio 4.12 Considere a escada esquematizada na seguinte figura e destinada a vencer
uma rampa de inclinao .

122

P
Note que os degraus da escada possuem dimenses que esto em progresso geomtrica de razo f , i.e., dado um degrau cujo cobertor de dimenso c, o degrau imediatamente acima tem um cobertor de dimenso f c. Defina a funo escada-progresso-geometrica
que recebe o ponto P , o ngulo , o cobertor c, o nmero de degraus n e o factor f e constri
a escada descrita no esquema anterior.

4.8

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 a que devia obedecer a construo
dos templos, em particular, no que diz respeito sua orientao, que devia ser de este para oeste, e no que diz respeito 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.
Para simplificar a nossa implementao, vamos ignorar estes detalhes e,
ao invs de distinguir vrios stilo, vamos simplesmente considerar o posicionamento de colunas distribudas linearmente segundo uma determinada
orientao, tal como esquematizamos na Figura 31.
At este momento, temos considerado as coordenadas meramente como
posies no espao. Agora, para a modelao deste templo, ser til adoptarmos uma perspectiva vectorial do conceito de coordenadas. Nesta perspectiva, as coordenadas passam a ser vistas como a extremidade de vectores posicionados na origem. Isto visvel na Figura 31 onde assinalmos as
posies de duas colunas do templo atravs dos vectores P e Q.
Estando todos os vectores posicionados na origem torna-se irrelevante
falar dela, o que nos permite caracterizar os vectores como tendo apenas
123

~v

Q
X

Figura 31: Planta de um templo com uma orientao arbitrria.


uma magnitude e uma direco. fcil verificarmos que esta magnitude
e direco so, precisamente, as coordenadas polares da extremidade do
vector, i.e., a distncia da origem extremidade do vector e o ngulo que o
vector faz com o eixo X.
A grande vantagem da perspectiva vectorial que ela permite-nos conceber uma lgebra para operar com vectores. Por exemplo, a soma de vectores o vector cujos componentes so a soma dos componentes correspondentes, i.e.,
P + Q = (Px + Qx , Py + Qy , Pz + Qz )
Do mesmo modo, podemos definir a subtraco de vectores atravs de
P Q = (Px Qx , Py Qy , Pz Qz )
e a multiplicao de um vector por um escalar :
P = (Px , Py , Pz )
Finalmente, a diviso de um vector por um escalar pode ser definida custa
da multiplicao pelo inverso do escalar, i.e.,
P
1
=P

Estas operaes esto esquematizadas graficamente na Figura 32 para


o caso bidimensional.
124

S =R

R = P + Q = S/
Q=RP
P =RQ

Figura 32: Operaes algbricas com vectores.


A traduo destas definies para Auto Lisp directa. Uma vez que
estamos criar operaes algbricas para coordenadas, vamos baptiz-las
combinando os nomes dos operadores aritmticos com a letra c (de coordenadas).
(defun +c (p0
(xyz (+ (cx
(+ (cy
(+ (cz

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

(defun -c (p0
(xyz (- (cx
(- (cy
(- (cz

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

(defun *c
(xyz (*
(*
(*

(p a)
(cx p) a)
(cy p) a)
(cz p) a)))

(defun /c (p a)
(*c p (/ 1.0 a)))
Exercicio 4.13 Um vector unitrio um vector de magnitude unitria. Defina a operao
vector-unitario que, dado um vector qualquer, calcula o vector unitrio que tem a
mesma direco que o vector dado.
Exercicio 4.14 O vector simtrico de um vector ~v o vector ~v 0 tal que ~v + ~v 0 = 0. Dito
de outro modo, o vector de igual magnitude mas direco oposta. Defina a operao
vector-simetrico que, dado um vector qualquer, calcula o vector simtrico do vector
dado.

Voltando agora ao problema do posicionamento das colunas no templo,


ilustrado na Figura 31, dado o vector ~v de orientao e separao de colunas, a partir da posio de qualquer coluna P , determinamos a posio da
125

coluna seguinte atravs de P + ~v . Este raciocnio permite-nos definir uma


primeira funo capaz de criar uma fileira de colunas. Esta funo ir usar,
como parmetros, as coordenadas P da base do eixo da primeira coluna,
a altura h da coluna, o vector ~v de separao entre os eixos das colunas e,
finalmente, o nmero n de colunas que pretendemos colocar. 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 no ponto P e, recursivamente,
colocamos as restantes colunas a partir do ponto que resulta de somarmos o vector de separao v ao ponto P . 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 h v n)
(if (= n 0)
nil
(progn
(coluna-dorica p h)
(colunas-doricas (+c p v)
h
v
(- n 1)))))

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 33:


Exercicio 4.15 Embora a utilizao do vector de separao entre colunas seja relativamente
simples, possvel simplificar ainda mais atravs do clculo desse vector a partir do pontos inicial e final da fileira de colunas. Usando a funo colunas-doricas, defina uma
funo denominada colunas-doricas-entre que, dados os centros P e Q da base das
colunas inicial e final, a altura h das colunas e, finalmente, o nmero de colunas, cria a
fileira de colunas entre aqueles dois centros.
A ttulo de exemplo, a imagem seguinte mostra o resultado da avaliao das seguintes
expresses:
(colunas-doricas-entre
(colunas-doricas-entre
(colunas-doricas-entre
(colunas-doricas-entre
(colunas-doricas-entre

(pol
(pol
(pol
(pol
(pol

10
10
10
10
10

0.0)
0.4)
0.8)
1.2)
1.6)

(pol
(pol
(pol
(pol
(pol

50
50
50
50
50

126

0.0)
0.4)
0.8)
1.2)
1.6)

8
8
8
8
8

6)
6)
6)
6)
6)

Figura 33: 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.

A partir do momento em que sabemos construir fileiras 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 posio das colunas extremas do templo e,
claro, a altura das colunas.
Em termos de algoritmo, vamos comear por construir um dos cantos
do peristilo, i.e., uma fronte e um lado:

127

(defun canto-peristilo-dorico (p altura v0 n0 v1 n1)


(colunas-doricas p altura v0 n0)
(colunas-doricas (+c p v1) altura v1 (- n1 1)))

Note-se que, para evitar repetir colunas, a segunda fileira comea na


segunda coluna e, logicamente, coloca menos uma coluna.
Para construirmos o peristilo completo, basta-nos construir um canto e,
de seguida, construir o outro canto mas com menos uma coluna em cada
lado e progredindo nas direces opostas.
(defun peristilo-dorico (p altura v0 n0 v1 n1)
(canto-peristilo-dorico p altura v0 n0 v1 n1)
(canto-peristilo-dorico
(+c p (+c (*c v0 (- n0 1)) (*c v1 (- n1 1))))
altura
(*c v0 -1) (- n0 1) (*c v1 -1) (- n1 1)))

Para um exemplo realista podemos considerar o templo de Segesta que


se encontra representado da Figura 11. 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) 9 (xy 4.8 0) 6 (xy 0 4.6) 14)

O resultado da avaliao da expresso anterior est representado na


Figura 34
Embora a grande maioria dos templos Gregos fosse de formato rectangular, tambm foram construdos templos de formato circular, a que chamaram Tholos. O Santurio de Atenas Pronaia, em Delfos, contm um bom
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 35.
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 36. 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.
128

Figura 34: 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.

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


quarto antes de Cristo. Fotografia de Michelle Kelley.

129

rp

ab
rb
x
rb
Figura 36: 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.
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
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.
130

ap
ab
rb

rp
y

rp

x
Figura 37: 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.
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 37. 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:

131

Figura 38: 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 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:
(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 38 mostra a imagem gerada pela avaliao da seguinte expresso:


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

Exercicio 4.16 Uma observao atenta do Tholos apresentado na Figura 38 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

132

evidente quando se compara uma vista de topo do desenho actual ( esquerda) com a
mesma vista daquele que seria o desenho correcto ( direita):

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.
Exercicio 4.17 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.
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.
Exercicio 4.18 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.

133

Exercicio 4.19 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.
Exercicio 4.20 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 seguinte imagem estereoscpica:36

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

4.9

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 39
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
36
Para visualizar a imagem estereoscpica, foque a ateno no meio das duas imagens e
cruze os olhos, como se quisesse focar um objecto muito prximo. Ir reparar que as duas
imagens passaram a ser quatro, embora ligeiramente desfocadas. Tente ento alterar o cruzar dos olhos de modo a s ver trs imagens, i.e., at que as duas imagens centrais fiquem
sobrepostas. Concentre-se nessa sobreposio e deixe os olhos relaxarem at a imagem ficar
focada.

134

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


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
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 40 esquematiza o processo para espirais feitas empregando quartos de circunferncia.
135

P1 ~v0

rf

P
~v1 ~v
rf f f
2
P2
P3

rf f

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

136

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 32 . 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)
(+ 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
137

Figura 41: O desenho da espiral.


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 41.


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


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
(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 41, temos


de avaliar a expresso:
(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 42:
(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 Sebasti139

Figura 42: Vrias espirais com razes de reduo de 0.9, 0.7 e 0.5, respectivamente.

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


ngulo de , 2 e 4 , respectivamente.
ano Serlio (semi-circunferncias), Giuseppe Salviati (quartos-de-circunferncia)
e Guillaume Philandrier (oitavos-de-circunferncia):37
(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 43.


Exercicio 4.21 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 geomtricos. A figura seguinte apresenta alguns
exemplos em que se variam sistematicamente alguns dos parmetros que caracterizam o
vulo:
37

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

140

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


apresenta na imagem seguinte:

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.

4.10

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
141

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


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 44, 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 testarmos 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 45. 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.

142

f c

f c
c

(x0 , y0 )
Figura 45: Parmetros de desenho de uma rvore.
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
(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

143

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


e factor de reduo 0.7.

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


factores de reduo do comprimento dos ramos.
(arvore (xy 0 0) 20 (/ pi 2) (/ pi 8) 0.7)

est representado na Figura 46.


A Figura 47 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)
(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
144

fe c

fd c
c

(x0 , y0 )
Figura 48: Parmetros de desenho de uma rvore com crescimento assimtrico.
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 48.
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)
(+ 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 49 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 fe145

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


factores de reduo do comprimento para os ramos esquerdo e direito.
nmenos 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.

Atribuio

Como vimos, a atribuio a capacidade de estabelecermos uma associao


entre um nome e uma entidade. Como iremos ver, a atribuio permite-nos
tambm alterar a associao entre um nome e uma entidade, fazendo com
que o nome passe a estar associado a uma outra entidade. Este conceito
introduz uma histria no valor do nome, pois passamos a poder falar do
valor antes da atribuio e do valor depois da atribuio.
Nesta seco vamos discutir mais em profundidade o conceito de atribuio. Para motivar vamos comear por introduzir um tpico importante
onde a atribuio tem um papel primordial: a aleatoriedade.

5.1

Aleatoriedade

Desenhar implica tomar decises conscientes que conduzam a um objectivo pretendido. Neste sentido, desenhar aparenta ser um processo racional
onde no h lugar para a aleatoriedade, a sorte ou a incerteza. De facto, nos
desenhos que temos feito at agora a racionalidade tem sido crucial, pois o
computador exige uma especificao rigorosa do que se pretende, no permitindo quaisquer ambiguidades. No entanto, sabido que um problema
de desenho frequentemente exige que o arquitecto experimente diferentes
solues antes de encontrar aquela que o satisfaz. Essa experimentao
passa por empregar alguma aleatoriedade no processo de escolha dos parmetros do desenho. Assim, embora um desenho final possa apresentar
uma estrutura que espelha uma inteno racional do arquitecto, o processo
que conduziu a esse desenho final no necessariamente racional e pode
ter passado por fases de ambiguidade e incerteza.
146

Quando a arquitectura se inspira na natureza surge um factor adicional


de aleatoriedade: em inmeros casos, a natureza intrinsecamente aleatria. Essa aleatoriedade, contudo, no total e est sujeita a restries.
Este facto facilmente compreendido quando pensamos que, embora no
existam dois pinheiros iguais, todos somos capazes de reconhecer o padro
que caracteriza um pinheiro. Em todas as suas obras, a natureza combina
estruturao e aleatoriedade. Nalguns casos, como o crescimento de cristais, por exemplo, h mais estruturao que aleatoriedade. Noutros casos,
como no comportamento de partculas subatmicas, h mais aleatoriedade
que estruturao. semelhana do que acontece na natureza, esta combinao de aleatoriedade e estruturao claramente visvel em inmeras
obras arquitecturais modernas.
Se pretendemos empregar computadores para o desenho e este desenho pressupe aleatoriedade, ento teremos de ser capazes de a incorporar
nos nossos algoritmos. A aleatoriedade pode ser incorporada de inmeras
formas, como, por exemplo:
A cor de um artefacto pode ser escolhida aleatoriamente.
O comprimento de uma parede pode ser um valor aleatrio entre determinados limites.
A deciso de dividir uma rea ou mant-la ntegra pode ser tomada
aleatoriamente.
A forma geomtrica a empregar para um determinado elemento arquitectnico pode ser escolhida aleatoriamente.

5.2

Nmeros Aleatrios

Em qualquer dos casos anteriores, podemos reduzir a aleatoriedade escolha de nmeros dentro de certos limites. Para uma cor aleatria, podemos
gerar aleatoriamente trs nmeros que representem os valores RGB38 da
cor. Para um comprimento aleatrio, podemos gerar aleatoriamente um
nmero dentro dos limites desse comprimento. Para uma deciso lgica,
podemos gerar aleatoriamente um nmero inteiro entre zero e um, decidindo de um modo se o nmero for zero, e de outro modo se o nmero for
um. Para uma escolha aleatria de entre um conjunto de alternativas podemos simplesmente gerar um nmero aleatrio entre um e o nmero de
elementos do conjunto e escolher a alternativa correspondente ao nmero
aleatrio gerado.
38

RGB a abreviatura de Red-Green-Blue, um modelo de cores em que qualquer cor vista


como uma combinao das trs cores primrias vermelho, verde e azul.

147

Estes exemplos mostram-nos que o fundamental conseguirmos gerar


um nmero aleatrio dentro de um certo intervalo. A partir dessa operao
podemos implementar todas as outras.
H dois processos fundamentais para se gerarem nmeros aleatrios.
O primeiro processo baseia-se na medio de processos fsicos intrnsecamente aleatrios, como seja o ruido electrnico ou o decaimento radioactivo. O segundo processo baseia-se na utilizao de funes aritmticas
que, a partir de um valor inicial (denominado a semente), produzem uma
sequncia de nmeros aparentemente aleatrios, sendo cada nmero da
sequncia gerado a partir do nmero anterior. Neste caso dizemos que
estamos perante um gerador de nmeros pseudo-aleatrios. O termo pseudo
justifica-se, pois, se repetirmos o valor da semente original, repetiremos
tambm a sequncia de nmeros de gerados.
Muito embora um gerador de nmeros pseudo-aleatrios gere uma sequncia de nmeros que, na verdade, no so aleatrios, ele possui duas importantes vantagens:
Pode ser facilmente implementado usando uma qualquer linguagem
de programao, no necessitando de outros mecanismos adicionais
para se obter a fonte de aleatoriedade.
frequente os nossos programas conterem erros. Para identificarmos a causa do erro pode ser necessrio reproduzirmos exactamente
as mesmas condies que deram origem ao erro. Para alm disso,
depois da correco do erro necessrio repetirmos novamente a
execuo do programa para nos certificarmos que o erro no ocorre
de novo. Infelizmente, quando o comportamento de um programa
depende de uma sequncia de nmeros verdadeiramente aleatrios
torna-se impossvel reproduzir as condies de execuo: da prxima
vez que executarmos o programa, a sequncia de nmeros aleatrios
ser quase de certeza diferente.
Por estes motivos, de agora em diante vamos apenas considerar geradores de nmeros pseudo-aleatrios, que iremos abusivamente designar
por geradores de nmeros aleatrios. Um gerador deste tipo caracteriza-se
por uma funo f que, a partir de um argumento xi , produz um nmero
xi+1 = f (xi ) aparentemente no relacionado com xi . A semente do gerador
o elemento x0 da sequncia.
Falta agora encontramos uma funo f apropriada. Para isso, entre outras qualidades, exige-se que os nmeros gerados por f sejam equiprovveis,
i.e., todos os nmeros dentro de um determinado intervalo tm igual probabilidade de serem gerados e que a sequncia de nmeros gerados tenha
um perodo to grande quanto possvel, i.e., que a sequncia dos nmeros
gerados s se comece a repetir ao fim de muito tempo.
148

Tm sido estudadas inmeras funes com estas caractersticas. A mais


utilizada denominada funo geradora congruencial linear que da forma39
xi+1 = (axi + b) mod m
Por exemplo, se tivermos a = 25173, b = 13849, m = 65536 e comearmos com uma semente qualquer, por exemplo, x0 = 12345, obtemos a
sequncia de nmeros pseudo-aleatrios40 2822, 11031, 21180, 42629, 27202,
49667, 50968, 33041, 37566, 43823, 2740, 43997, 57466, 29339, 39312, 21225,
61302, 58439, 12204, 57909, 39858, 3123, 51464, 1473, 302, 13919, 41380,
43405, 31722, 61131, 13696, 63897, 42982, 375, 16540, 25061, 24866, 31331,
48888, 36465, . . .
Podemos facilmente confirmar estes resultados usando o Lisp:
(defun proximo-aleatorio (ultimo-aleatorio)
(rem (+ (* 25173 ultimo-aleatorio) 13849)
65536))
_$ (proximo-aleatorio 12345)
2822
_$ (proximo-aleatorio 2822)
11031
_$ (proximo-aleatorio 11031)
21180

Esta abordagem, contudo, implica que s podemos gerar um nmero


aleatrio x1+i se nos recordarmos do nmero xi gerado imediatamente
antes para o podermos dar como argumento funo proximo-aleatorio.
Infelizmente, o momento e o ponto do programa em que podemos precisar
de um novo nmero aleatrio pode ocorrer muito mais tarde e muito mais
longe do que o momento e o ponto do programa em que foi gerado o ltimo
nmero aleatrio, o que complica substancialmente a escrita do programa.
Seria prefervel que, ao invs da funo proximo-aleatorio, que depende do ltimo valor gerado xi , tivssemos uma funo aleatorio que
no precisasse de receber o ltimo nmero gerado para conseguir gerar o
prximo nmero. Assim, em qualquer ponto do programa em que precisssemos de gerar um novo nmero aleatrio, limitvamo-nos a invocar a
funo aleatorio sem termos de nos recordar do ltimo nmero gerado.
Partindo do mesmo valor da semente, teriamos:
39

O operador mod implementa o modulo, correspondente ao resto da diviso do primeiro


operando (o dividendo, esquerda do operador) pelo segundo operando (o divisor, direita do operador) e tendo o mesmo sinal do divisor. O operador rem implementa o remainder, correspondente ao resto da diviso do dividendo pelo divisor, mas tendo o mesmo sinal
do dividendo. Nem todas as linguagens implementam ambos os operadores. A linguagem
Auto Lisp apenas implementa o operador rem.
40
Na verdade, esta sequncia no suficientemente aleatria pois existe um padro que
se repete continuamente. Consegue descobri-lo?

149

_$ (aleatorio)
2822
_$ (aleatorio)
11031
_$ (aleatorio)
21180

5.3

Estado

A funo aleatorio apresenta um comportamento diferente do das funes que vimos at agora. At este momento, todas as funes que tnhamos definido comportavam-se como as funes matemticas tradicionais:
dados argumentos, a funo produz resultados e, mais importante, dados
os mesmos argumentos, a funo produz sempre os mesmos resultados. Por
exemplo, independentemente do nmero de vezes que invocarmos a funo quadrado, para um dado argumento ela ir sempre produzir o quadrado desse argumento.
A funo aleatorio diferente de todas as outras, pois, para alm de
no precisar de argumentos, ela produz um resultado diferente de cada vez
que invocada.
Do ponto de vista matemtico, uma funo sem parmetros no tem
nada de anormal: precisamente aquilo que se designa por uma constante.
De facto, tal como escrevemos sin cos x para designar sin(cos(x)), tambm
escrevemos sin para designar sin(()), onde se v que pode ser interpretado como uma funo sem argumentos.
Do ponto de vista matemtico, uma funo que produz resultados diferentes a cada invocao no tem nada de normal: uma aberrao, pois,
de acordo com a definio matemtica de funo, esse comportamento no
possvel. E, no entanto, precisamente este o comportamento que gostaramos de ter na funo aleatorio.
Para se obter o pretendido comportamento necessrio introduzir o
conceito de estado. Dizemos que uma funo tem estado quando o seu comportamento depende da sua histria, i.e., das suas invocaes anteriores. A
funo aleatrio um exemplo de uma funo com estado, mas h inmeros outros exemplos no mundo real. Uma conta bancria tambm tem
um estado que depende de todas as transaces anteriores que lhe tiverem
sido feitas. O depsito de combustvel de um carro tambm tem um estado
que depende dos enchimentos e dos trajectos realizados.
Para que uma funo possa ter histria necessrio que tenha memria,
i.e., que se possa recordar de acontecimentos passados para assim poder
influenciar os resultados futuros. At agora vimos que o operador setq
nos permitia definir associaes entre nomes e valores, mas o que ainda no
tnhamos discutido a possibilidade desse operador modificar associaes,
i.e., alterar o valor que estava associado a um determinado nome. esta
possibilidade que nos permite incluir memria nas nossas funes.
150

No caso da funo aleatorio vimos que a memria que nos interessa


saber qual foi o ltimo nmero aleatrio gerado. Imaginemos ento que
tnhamos uma associao entre o nome ultimo-aleatorio-gerado e
esse nmero. No momento inicial, naturalmente, esse nome dever estar
associado semente da sequncia de nmeros aleatrios. Para isso, definimos:
_$ (setq ultimo-aleatorio-gerado 12345)

De seguida, j podemos definir a funo aleatorio que no s usa


o ltimo valor associado quele nome como actualiza essa associao com
o novo valor gerado, i.e.:41
(defun aleatorio ()
(setq ultimo-aleatorio-gerado
(proximo-aleatorio ultimo-aleatorio-gerado)))
_$ (aleatorio)
2822
_$ (aleatorio)
11031

Como se v, de cada vez que a funo aleatorio invocada, o valor


associado a ultimo-aleatorio-gerado actualizado, permitindo assim influenciar o futuro comportamento da funo. Obviamente, em qualquer momento, podemos re-iniciar a sequncia de nmeros aleatrios simplesmente repondo o valor da semente:
_$ (aleatorio)
21180
_$ (aleatorio)
42629
_$ (setq ultimo-aleatorio-gerado 12345)
12345
_$ (aleatorio)
2822
_$ (aleatorio)
11031
_$ (aleatorio)
21180

5.4

Estado Local e Estado Global

Vimos anteriormente que uma funo pode ter variveis locais bastando,
para isso, a sua indicao a seguir aos parmetros da funo. Cada varivel local possui um nome que apenas visvel para a funo que a contm.42 Acontece que no caso da funo aleatorio, ela usa uma vari41

Note-se que o operador setq, para alm de estabelecer uma associao entre um nome
e um valor devolve o valor que ficou associado.
42
Esta afirmao no inteiramente verdade para o Auto Lisp. Mais frente explicaremos
aquilo que verdadeiramente acontece no caso do Auto Lisp.

151

vel denominada ultimo-aleatorio-gerado que no referida na lista


de parmetros da funo. Consequentemente, no uma varivel local,
dizendo-se ento que uma varivel livre.
Quando uma varivel livre visvel de todos os lados, tal como acontece com ultimo-aleatorio-gerado, dizemos que se trata de uma varivel global. Ao conjunto de todas as variveis globais de um programa
chama-se estado global pois, em cada instante, elas determinam o estado do
programa.
Uma varivel global , potencialmente, uma enorme fonte de problemas, pois pode ser alterada em qualquer momento e a partir de qualquer
ponto do programa. Quando essa alterao incorrecta pode ser muito
dificil saber quando e onde que o erro foi cometido.
No entanto, nalguns casos como o da funo aleatorio, a nica forma
de podermos dar memria s funes atravs de variveis globais. De
facto, em Auto Lisp, as variveis locais possuem durao dinmica, i.e., apenas existem enquanto a funo que as introduz est a ser invocada (e existem ocorrncias diferentes dessas variveis para diferentes invocaes da
funo). Por este motivo, em Auto Lisp, uma varivel local no pode nunca
ser usada para manter uma memria que dure mais do que a invocao da
funo. Isto implica que no possvel termos estado local em Auto Lisp,
i.e., no possvel termos um conjunto de variveis que descrevem o estado de uma funo mas que no so visiveis globalmente.43
Por estes motivos, em Auto Lisp, a nica possibilidade que temos de
dar memria s nossas funes atravs da utilizao de variveis globais.
Pelos problemas apontados devemos, contudo, ter extremo cuidado com
a utilizao de variveis globais e devemos estar alerta para a possibilidade de ocorrerem conflitos de nomes, em que duas funes independentes pretendem definir uma mesma varivel global.44 Para evitar este ltimo
problema, as nossas variveis globais devem ter nomes perfeitamente explcitos e com pouca probabilidade de aparecerem repetidos em contextos
diferentes.

5.5

Escolhas Aleatrias

Se observarmos a definio da funo proximo-aleatorio constatamos


que a sua ltima operao computar o resto da diviso por 65536, o
que implica que a funo produz sempre valores entre 0 e 65535. Embora (pseudo) aleatrios, estes valores esto contidos num intervalo que
s muito excepcionalmente ser til. Na realidade, muito mais frequente
43

H outros dialectos de Lisp e muitas outras linguagens de programao que permitem


o estado local.
44
Alguns dialectos de Lisp possuem mecanismos que permitem evitar estes conflitos. O
Auto Lisp, infelizmente, no possui qualquer destes mecanismos.

152

precisarmos de nmeros aleatrios contidos em intervalos muito mais pequenos. Por exemplo, se pretendermos simular o lanar de uma moeda
ao ar, estaremos interessados em ter apenas os nmeros aleatrios 0 ou 1,
representando caras ou coroas.
Tal como os nmeros aleatrios que produzimos so limitados ao intervalo [0, 65536[ pela obteno do resto da diviso por 65536, tambm agora
podemos voltar aplicar a mesma operao para produzir um intervalo mais
pequeno. Assim, no caso do lanamento de uma moeda ao ar, podemos
simplesmente usar a funo aleatorio para gerar um nmero aleatrio
e, de seguida, aplicamos-lhe o resto da diviso por dois. No caso geral, em
que pretendemos nmeros aleatrios no intervalo [0, x[, aplicamos o resto
da diviso por x. Assim, podemos definir uma nova funo que gera um
nmero aleatrio entre zero e o seu parmetro e que, por tradio, iremos
baptizar de random:
(defun random (x)
(rem (aleatorio) x))

Note-se que a funo random nunca deve receber um argumento maior


que 65535 pois isso faria a funo perder a caracterstica da equiprobabilidade
dos nmeros gerados: todos os nmeros superiores a 65535 tero probabilidade zero de ocorrerem.45
agora possvel simularmos inmeros fenmenos aleatrios como, por
exemplo, o lanar de uma moeda:
(defun cara-ou-coroa ()
(if (= (random 2) 0)
"cara"
"coroa"))

Infelizmente, quando testamos repetidamente a nossa funo, o seu


comportamento parece muito pouco aleatrio:
_$ (cara-ou-coroa)
"cara"
_$ (cara-ou-coroa)
"coroa"
_$ (cara-ou-coroa)
"cara"
_$ (cara-ou-coroa)
"coroa"
_$ (cara-ou-coroa)
"cara"
_$ (cara-ou-coroa)
"coroa"

Na realidade, os resultados que obtemos so uma repetio sistemtica


45

Na verdade, o limite superior deve ser bastante inferior ao limite da funo aleatorio
para manter a equiprobabilidade dos resultados.

153

do par cara/coroa, o que mostra que a expresso (random 2) se limita


a gerar a sequncia:
01010101010101010101010101010101010101010110101010101010101

O mesmo fenmeno ocorre para outros limites do intervalo de gerao.


Por exemplo, a expresso (random 4) deveria gerar um nmero aleatrio
no conjunto {0, 1, 2, 3} mas a sua aplicao repetida gera a seguinte sequncia de nmeros:
01230123012301230123012301230123012301230123012301230123012

Embora a equiprobabilidade dos nmeros seja perfeita, evidente que


no h qualquer aleatoriedade.
O problema das duas sequncias anteriores est no facto de terem um
perodo muito pequeno. O perodo nmero de elementos que so gerados
antes de o gerador entrar em ciclo e voltar a repetir os mesmos elementos
que gerou anteriormente. Obviamente, quanto maior for o perodo, melhor
o gerador de nmeros aleatrios e, neste sentido, o gerador que apresentmos de baixa qualidade.
Enormes quantidades de esforo tm sido investidas na procura de bons
geradores de nmeros aleatrios e, embora os melhores sejam produzidos
usando mtodos bastante mais sofisticados do que os que empregmos at
agora, tambm possvel encontrar um bom gerador congruencial linear
desde que se faa uma judiciosa escolha dos seus parmetros. De acordo
com [?], o gerador congruencial linear
xi+1 = (axi + b) mod m
pode constituir um bom gerador pseudo-aleatrio desde que tenhamos a =
16807, b = 0 e m = 231 1 = 2147483647. Uma traduo directa da definio
matemtica produz a seguinte funo Lisp:
(defun proximo-aleatorio (ultimo-aleatorio)
(rem (* 16807 ultimo-aleatorio)
2147483647))

Infelizmente, quando testamos a funo aleatorio, obtemos resultados bizarros: 64454845, 960821323, 553057299, 924795749, 444490781,
. . . . Sendo o parmetro ultimo-aleatorio inicialmente positivo e sendo
o produto e o resto da diviso obtidos usando operandos positivos, o resultado teria necessariamente de ser no-negativo. Uma vez que existem
valores negativos na sequncia, isso sugere que est a ocorrer o mesmo
fenmeno que discutimos na seco 2.16: overflow, i.e., o resultado da multiplicao , por vezes, um nmero demasiado grande para a capacidade
de representao do Auto Lisp. Para resolver este problema, inventou-se
154

um algoritmo (ver [?]) capaz de computar os nmeros sem exceder a capacidade da mquina, desde que esta seja capaz de representar todos os inteiros no intervalo [231 , 231 1], ou seja, [2147483648, 2147483647] que
precisamente a gama de valores representveis em Auto Lisp. O algoritmo
baseia-se simplesmente em garantir que todos os valores intermdios do
clculo so sempre representveis pelo Auto Lisp.
(defun proximo-aleatorio (ultimo-aleatorio / teste)
(setq teste (- (* 16807 (rem ultimo-aleatorio 127773))
(* 2836 (/ ultimo-aleatorio 127773))))
(setq ultimo-aleatorio
(if (> teste 0)
teste
(+ teste 2147483647))))

Esta definio tem a vantagem de permitir gerar aleatoriamente todos


os inteiros representveis pela linguagem. Usando esta definio da funo, a repetida avaliao da expresso (random 2) produz a sequncia:
01001010001011110100100011000111100110110101101111000011110

e, para a expresso (random 4), produz:


21312020300221331333233301112313001012333020321123122330222

razoavelmente evidente que qualquer das sequncias geradas tem


agora um perodo demasiado grande para se conseguir detectar qualquer
padro de repetio. De ora em diante, ser esta a definio de proximo-aleatorio
que iremos empregar.
5.5.1

Nmeros Aleatrios Fraccionrios

O processo de gerao de nmeros aleatrios que implementmos apenas


capaz de gerar nmeros aleatrios do tipo inteiro. No entanto, tambm frequente precisarmos de gerar nmeros aleatrios fraccionrios, por
exemplo, no intervalo [0, 1[.
Para combinar estes dois requisitos, usual no mundo do Lisp que a
funo random receba o limite superior de gerao x e o analise para determinar se inteiro ou real, devolvendo um valor aleatrio adequado em
cada caso. Para isso, no preciso mais do que mapear o intervalo de gerao de inteiros que, como vimos, [0, 2147483647[, no intervalo [0, x[. A
implementao trivial:46
(defun random (x)
(if (realp x)
(* x (/ (aleatorio) 2147483647.0))
(rem (aleatorio) x)))
46

Esta funo usa o reconhecedor universal realp que foi definido na seco 2.29.

155

5.5.2

Nmeros Aleatrios num Intervalo

Se, ao invs de gerarmos nmeros aleatrios no intervalo [0, x[, preferirmos gerar nmeros aleatrios no intervalo [x0 , x1 [, ento basta-nos gerar
um nmero aleatrio no intervalo [0, x1 x0 [ e somar-lhe x0 . A funo
random-[] implementa esse comportamento:
(defun random-[] (x0 x1)
(+ x0 (random (- x1 x0))))

Tal como a funo random, tambm random-[] produz um valor real


no caso de algum dos limites ser real.
Para visualizarmos um exemplo de utilizao desta funo, vamos recuperar a funo arvore que modelava uma rvore, cuja definio era:
(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)
(+ 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))))

Para incorporarmos alguma aleatoriedade neste modelo de rvore podemos considerar que os ngulos de abertura dos ramos e os factores de
reduo de comprimento desses ramos no possuem valores constantes ao
longo da recurso, antes variando dentro de certos limites. Assim, em vez
de nos preocuparmos em ter diferentes aberturas e factores para o ramo
esquerdo e direito, teremos simplesmente variaes aleatrias em ambos:

156

Figura 50: Vrias rvores geradas com ngulos de abertura aleatrios no

[ e factores de reduo do comprimento aleatrios no interintervalo [ 2 , 16


valo [0.6, 0.9[.
(defun arvore (base comprimento angulo
min-alfa max-alfa min-f max-f
/ topo)
(setq topo (+pol base comprimento angulo))
(ramo base topo)
(if (< comprimento 2)
(folha topo)
(progn
(arvore topo
(* comprimento (random-[] min-f max-f))
(+ angulo (random-[] min-alfa max-alfa))
min-alfa max-alfa min-f max-f)
(arvore topo
(* comprimento (random-[] min-f max-f))
(- angulo (random-[] min-alfa max-alfa))
min-alfa max-alfa min-f max-f))))

Usando esta nova verso, podemos gerar inmeras rvores semelhantes e, contudo, suficientemente diferentes para parecerem bastante mais
naturais. As rvores apresentadas na Figura 50 foram geradas usando exactamente os mesmos parmetros de crescimento:
(arvore
(arvore
(arvore
(arvore
(arvore
(arvore

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

0
0) 20 (/
150
0) 20 (/
300
0) 20 (/
0 150) 20 (/
150 150) 20 (/
300 150) 20 (/

pi
pi
pi
pi
pi
pi

2)
2)
2)
2)
2)
2)

(/
(/
(/
(/
(/
(/

157

pi
pi
pi
pi
pi
pi

16)
16)
16)
16)
16)
16)

(/
(/
(/
(/
(/
(/

pi
pi
pi
pi
pi
pi

4)
4)
4)
4)
4)
4)

0.6
0.6
0.6
0.6
0.6
0.6

0.9)
0.9)
0.9)
0.9)
0.9)
0.9)

Exercicio 5.1 As rvores produzidas pelas funo arvore so pouco realista pois so totalmente bidimensionais, com troncos que so simples linhas e folhas que so pequenas
circunferncias. O clculo das coordenadas dos ramos e folhas tambm bidimensional,
assentando sobre coordenadas polares que so dadas pelos parmetros comprimento e
angulo.
Pretende-se que redefina as funes ramo, folha e arvore de modo a aumentar o
realismo das rvores geradas.
Para simplificar, considere que as folhas podem ser simuladas por pequenas esferas
e que os ramos podem ser simulados por troncos de cone, cujo raio da base 10% do
comprimento do tronco e cujo raio do topo 90% do raio da base.
Para que a gerao de rvores passe a ser verdadeiramente tridimensional, redefina
tambm a funo arvore de modo a que o topo de cada ramo seja um ponto em coordenadas esfricas escolhidas aleatoriamente a partir da base do ramo. Isto implica que a funo
rvore, ao invs de ter dois parmetros para o comprimento e o ngulo das coordenadas
polares, precisar de ter trs, para o comprimento, a longitude e a co-latitude. Do mesmo
modo, ao invs de receber os quatro limites para a gerao de comprimentos e ngulos
aleatrios, precisar de seis limites para os trs parmetros.
Experimente diferentes argumentos para a sua redefinio da funo arvore de modo
a gerar imagens como a seguinte:

Exercicio 5.2 Defina uma funo denominada cilindros-aleatorios que recebe como
argumento um nmero de cilindros n e que gera n cilindros colocados em posies aleatrias, com raios aleatrios e comprimentos aleatrios, tal como se apresenta na seguinte
imagem:

158

Exercicio 5.3 Uma caminhada aleatria uma formalizao do movimento de uma particula
que est sujeita a impulsos de intensidade aleatria vindos de direces aleatrias. este
fenmeno que acontece, por exemplo, a um gro de plen cado na gua: medida que as
molculas da gua vo chocando com ele, o gro vai se movendo aleatoriamente.
A seguinte imagem mostra trs caminhadas aleatrias:

Considere uma modelao da caminhada aleatria num plano bi-dimensional. Defina a


funo caminhada-aleatoria que recebe o ponto inicial P da caminhada, a distncia
mxima d que a particula pode percorrer quando impulsionada e o nmero n de impulsos sucessivos que a partcula vai receber. Note que, a cada impulso, a particula desloca-se,
numa direco aleatria, uma distncia aleatria entre zero e a distncia mxima. A sua
funo dever simular a caminhada aleatria correspondente, tal como se apresenta na figura anterior que foi criada por trs execues diferentes desta funo. Da esquerda para a
direita, foram usados os parmetros d = 5, n = 100, d = 2, n = 200, e d = 8, n = 50,
Exercicio 5.4 Defina uma funo cara-ou-coroa viciada de tal forma que, embora a funo produza aleatoriamente a string cara ou a string coroa, a cara sai, em mdia,
apenas 30% das vezes em que a funo invocada.
Exercicio 5.5 Defina uma funo denominada blocos-conexos capaz de contruir um
aglomerado de blocos conexos, i.e., onde os blocos esto fisicamente ligados entre si, tal
como se v na seguinte imagem:

159

Note que os blocos adjacentes possuem sempre orientaes ortogonais.

5.6

Planeamento Urbano

As cidades constituem um bom exemplo de combinao entre estruturao


e aleatoriedade. Embora muitas das cidades mais antigas aparentem ter
uma forma catica resultante do seu desenvolvimento no ter sido planeado, a verdade que desde muito cedo se sentiu a necessidade de estruturar a cidade de modo a facilitar a sua utilizao e o seu crescimento, sendo
conhecidos exemplos de cidades planeadas desde 2600 antes de Cristo.
Embora haja bastantes variaes, as duas formas mais usuais de planear uma cidade so atravs do plano ortogonal ou atravs do plano circular. No plano ortogonal, as avenidas so rectas e fazem ngulos rectos
entre si. No plano circular as avenidas principais convergem numa praa
central e as avenidas secundrios desenvolvem-se em crculos concntricos
em torno deste ponto, acompanhando o crescimento da cidade. A Figura 51
apresenta um bom exemplo de uma cidade essencialmente desenvolvida a
partir de planos ortogonais. Nesta seco vamos explorar a aleatoriedade
para produzir variaes em torno destes planos.
Num plano ortogonal, a cidade organiza-se em termos de quarteires,
em que cada quarteiro assume uma forma quadrada ou rectangular e
pode conter vrios edifcios. Para simplificar, vamos assumir que cada
quarteiro ser de forma quadrada e ir conter um nico edifcio. Cada
160

Figura 51: Vista area da cidade de New York nos Estados Unidos. Fotografia de Art Siegel.
edifcio ter uma largura determinada pela largura do quarteiro e uma altura mxima imposta. Os edifcios sero separados uns dos outros por ruas
com uma largura fixa.
Para estruturarmos a funo que constri a cidade em malha ortogonal,
vamos decompor o processo na construo de sucessivas ruas. A construo de cada rua ser ento decomposta na construo dos sucessivos
prdios. Assim, teremos de parametrizar a funo com o nmero de ruas
a construir na cidade e com o nmero de prdios por rua. Para alm disso
iremos necessitar de saber as coordenadas do ponto p onde se comea a
construir a cidade, a largura e a altura de cada prdio, respectivamente,
l-predio e a-predio e, finalmente, a largura da rua l-rua. A funo
ir construir uma faixa de prdios seguido de uma rua e, recursivamente,
construir as restantes faixas de prdios e ruas no ponto correspondente
faixa seguinte. Para simplificar, vamos assumir que as ruas estaro alinhadas com os eixos x e y, pelo que cada nova rua corresponde a um deslocamento ao longo do eixo y e cada novo prdio corresponde a um deslocamento ao longo do eixo x. Assim, temos:

161

(defun malha-predios (p ruas predios l-predio a-predio l-rua)


(if (= ruas 0)
nil
(progn
(rua-predios p predios l-predio a-predio l-rua)
(malha-predios (+y p (+ l-predio l-rua))
(1- ruas)
predios
l-predio
a-predio
l-rua))))

Para a construo das ruas com prdios, o racioccio idntico: colocamos um prdio nas coordenadas correctas e, recursivamente, colocamos os
restantes prdios aps o deslocamento correspondente. A seguinte funo
implementa este processo:
(defun rua-predios (p predios l-predio a-predio l-rua)
(if (= predios 0)
nil
(progn
(predio p l-predio a-predio)
(rua-predios (+x p (+ l-predio l-rua))
(1- predios)
l-predio
a-predio
l-rua))))

Finalmente, precisamos de definir a funo que cria um prdio. Para


simplificar, vamos model-lo como um paralelippedo:
(defun predio (p l-predio a-predio)
(command "_.box" p (+xyz p l-predio l-predio a-predio)))

Com estas trs funes j podemos experimentar a construo de uma


nova urbanizao. Por exemplo, a seguinte expresso cria um arranjo de
dez ruas com dez prdios por rua:
(malha-predios (xyz 0 0 0) 10 10 100 400 40)

O resultado est representado na Figura 52.


Como bvio pela Figura 52, a urbanizao produzida pouco elegante
pois falta-lhe alguma da aleatoriedade que caracteriza as cidades.
Para incorporarmos essa aleatoriedade vamos comear por considerar
que a altura dos prdios pode variar aleatoriamente entre a altura mxima
dada e um dcimo dessa altura. Para isso, redefinimos a funo predio:

162

Figura 52: Uma urbanizao em malha ortogonal com cem edifcios todos
da mesma altura.
(defun predio (p l-predio a-predio)
(command "_.box"
p
(+xyz p
l-predio
l-predio
(* (random-[] 0.1 1.0) a-predio))))

Usando exactamente os mesmos parmetros anteriores em duas avaliaes sucessivas da mesma expresso, conseguimos agora construir as urbanizaes esteticamente mais apelativas representadas nas Figuras 53 e
54.
Exercicio 5.6 As urbanizaes produzidas pelas funes anteriores no apresentam variabilidade suficiente, pois os edifcios tm todos a mesma forma. Para melhorar a qualidade
esttica da urbanizao pretende-se empregar diferentes funes para a construo de diferentes tipos de edifcios: a funo predio0 dever construir um paralelippedo de altura
aleatria tal como anteriormente e a funo predio1 dever construir uma torre cilndrica
de altura aleatria e contida no espao de um quarteiro. Defina estas duas funes.
Exercicio 5.7 Pretende-se que use as duas funes predio0 e predio1 definidas no exerccio anterior para a redefinio da funo predio de modo a que esta, aleatoriamente,
construa prdios diferentes. A urbanizao resultante dever ser constituda aproximadamente por 20% de torres circulares e 80% de prdios rectangulares, tal como exemplificado
na imagem seguinte:

163

Figura 53: Uma urbanizao em malha ortogonal com cem edifcios com
alturas aleatrias.

Figura 54: Uma urbanizao em malha ortogonal com cem edifcios com
alturas aleatrias.

164

Figura 55: Vista de alguns prdios de Manhattan. Fotografia de James K.


Poole.

Exercicio 5.8 As cidades criadas nos exerccios anteriores apenas permitem dois tipos de
edifcios: torres paralelipipdicas ou torres cilndricas. Contudo, quando observamos uma
cidade real como a apresentada na Figura 55, verificamos que existem prdios com muitas outras formas pelo que, para aumentarmos o realismo das nossas simulaes, teremos
de implementar um vasto nmero de funes, cada uma capaz de construir um edifcio
diferente.
Felizmente, uma observao atenta da Figura 55 mostra que, na verdade, muitos dos
prdios seguem um padro e podem ser modelado por paralelippedos sobrepostos com
dimenses aleatrias mas sempre sucessivamente mais pequenos em funo da altura, algo
que podemos facilmente implementar com uma nica funo.
Considere que este modelo de edifcio parametrizado pelo nmero de blocos sobrepostos pretendidos, pelas coordenadas do primeiro bloco e pelo comprimento, largura

165

e altura do edifcio. O bloco de base tem exactamente o comprimento e largura especificados mas a sua altura dever estar entre 20% e 80% da altura total do edifcio. Os blocos
seguintes esto centrados sobre o bloco imediatamente abaixo e possuem um comprimento
e largura que esto entre 70% e 100% dos parmetros correspondentes no bloco imediatamente abaixo. A altura destes blocos dever ser entre 20% e 80% da altura restante do
edifcio. A imagem seguinte mostra alguns exemplos deste modelo de edifcios:

Com base nesta especificao, defina a funo predio-blocos e use-a para redefinir
a funo predio0 de modo a que sejam gerados prdios com um nmero aleatrio de
blocos sobrepostos. Com esta redefinio, a funo malha-predios dever ser capaz de
gerar urbanizaes semelhantes imagem seguinte, em que se empregou para cada prdio
um nmero de blocos entre 1 e 6:

Exercicio 5.9 Em geral, as cidades possuem um ncleo de edifcios relativamente altos no


centro e, medida que nos afastamos para a periferia, a altura tende a diminuir, sendo este
fenmeno perfeitamente visvel na Figura 51.
A variao da altura dos edifcios pode ser modelada por diversas funes matemticas
mas, neste exerccio, pretende-se que empregue uma distribuio Gaussiana bi-dimensional

166

dada por


o )2 +( yyo )2
( xx
x
y

f (x, y) = e

em que f o factor de ponderao da altura, (x0 , y0 ) so as coordenadas do ponto mais alto


da superfcie Gaussiana e 0 e 1 so os factores que afectam o alargamento bi-dimensional
dessa superfcie. Para simplificar, assuma que o centro da cidade fica nas coordenadas (0, 0)
e que x = y = 25l, sendo l a largura do edifcio. A imagem seguinte mostra o aspecto de
uma destas urbanizaes:

Incorpore esta distribuio no processo de construo de cidades para produzir cidades


mais realistas.
Exercicio 5.10 Frequentemente, as cidades possuem mais do que um ncleo de edifcios
altos. Estes ncleos encontram-se separados uns dos outros por zonas de edifcios menos
altos. Tal como no exerccio anterior, cada ncleo de edifcios pode ser modelado por uma
distribuio Gaussiana. Admitindo a independncia dos vrios ncleos, a altura dos edifcios pode ser modelada por distribuies Gaussianas sobrepostas, i.e., em cada ponto, a
altura do edifcio ser o mximo das alturas das distribuies Gaussianas dos vrios ncleos.
Utilize a abordagem anterior para exemplificar a modelao de uma cidade com trs
ncleos de arranha-cus, semelhana do que se apresenta na imagem seguinte:

167

Exercicio 5.11 Pretende-se criar um conjunto de n esferas tangentes a uma esfera virtual
de centro em p e raio limite rl . As esferas possuem centros a uma distncia de p que um
valor aleatrio compreendido entre um raio interior ri e um raio exterior re , tal como se
pode visualizar no seguinte esquema:

re
p

ri
rl

Defina uma funo denominada esferas-na-esfera que, a partir do centro p, dos


raios ri , re , e rl e ainda do nmero n, cria este conjunto de esferas, permitindo produzir
imagens como as que se seguem:

Listas

Vimos anteriormente que uma lista uma sequncia de elementos que podemos construir usando a funo list. Vimos tambm que podemos aceder a cada um destes elementos utilizando a funo nth que, dada a posio de um elemento e a lista que o contm, devolve esse elemento. Eis um
exemplo do uso destas operaes:
_$ (setq
("Filipa"
_$ (nth 0
"Filipa"
_$ (nth 3
"Maria"

amigos (list "Filipa" "Pedro" "Carlos" "Maria"))


"Pedro" "Carlos" "Maria")
amigos)
amigos)

No passado, usmos as listas para implementar coordenadas tridimensionais, i.e., agrupamentos de trs nmeros representando posies no espao. Agora, vamos us-las para agrupar um nmero arbitrrio de elementos. Para isso, temos de adoptar uma viso diferente das listas e temos de
168

introduzir novas funes que nos permitem trabalhar mais facilmente com
listas. Comecemos por introduzir uma funocons, abreviatura da palavra constructque nos permite criar uma lista maior a partir de um novo
elemento e de uma lista j existente.
_$ (setq novos-amigos (cons "Bernardo" amigos))
("Bernardo" "Filipa" "Pedro" "Carlos" "Maria")
_$ amigos
("Filipa" "Pedro" "Carlos" "Maria")

Como se podemos ver, a funo cons junta um elemento a uma lista,


produzindo uma nova lista que tem mais esse elemento cabea da lista.
Notemos tambm que a lista original fica inalterada pela funo cons.
Para decompormos listas, o Lisp fornece um par de funescar
e cdrque podem ser vistas como o inverso da funo cons. Enquanto
a funo cons junta um elemento a uma lista, a funo car indica qual
o elemento que se juntou e a funo cdr indica qual a lista a que ele se
juntou. Eis um exemplo:
_$ (car novos-amigos)
"Bernardo"
_$ (cdr novos-amigos)
("Filipa" "Pedro" "Carlos" "Maria")

Dito de outra forma, podemos afirmar que a funo car devolve o primeiro elemento da lista e a funo cdr devolve o resto da lista, i.e., a lista
aps o primeiro elemento. 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 que vem Antes e
o cDr obtm o que vem Depois.
Naturalmente, as funes car e cdr podem ser encadeadas:
_$ (car (cdr novos-amigos))
"Filipa"
_$ (cdr (cdr (cdr novos-amigos)))
("Carlos" "Maria")
_$ (car (cdr (cdr (cdr novos-amigos))))
"Carlos"

Dado que aquele gnero de expresses muito utilizado em Lisp, foram


compostas as vrias combinaes, e criaram-se funes do tipo (caddr exp),
que correspondem a (car (cdr (cdr exp))). O nome da funo indica
quais as operaes a realizar. Um a representa um car e um d representa um cdr.
A partir dos exemplos anteriores, lgico deduzir que a funo list
faz algo de muito semelhante a uma combinao de cons e, na verdade,
assim :
169

_$
(1
_$
(1
_$
(1
_$
(1

(cons 1
2 3 4)
(cons 1
2 3 4)
(cons 1
2 3 4)
(cons 1
2 3 4)

(list 2 3 4))
(cons 2 (list 3 4)))
(cons 2 (cons 3 (list 4))))
(cons 2 (cons 3 (cons 4 (list)))))

Anteriormente, vimos tambm que o Lisp considera que o smbolo nil


representa uma lista sem elementos e que o predicado null identifica estas
listas. De facto, quando tentamos obter o resto de uma lista com um nico
elemento, obtemos nil, tal como quando tentamos construir uma lista sem
elementos:
_$ (cdr (list 1))
nil
_$ (list)
nil
_$ (null (list))
T
_$ (null amigos)
nil

Embora o Auto Lisp use o smbolo nil para representar uma lista vazia,
sabemos que este smbolo tambm usado para representar a falsidade,
tal como a ltima expresso demonstra. Embora internamente seja exactamente a mesma coisa, para distinguir os dois usos o Auto Lisp permite
uma notao diferente para o smbolo nil: (). Isso visvel na seguinte
interaco:
_$ ()
nil
_$ (null ())
T

6.1

Pares

Vimos que a funo cons permite juntar um elemento a uma lista mas,
na verdade, ela faz algo bastante mais fundamental. Esta funo aceita
quaisquer duas entidades como argumentos e produz um par com essas
duas entidades, i.e., um valor que representa um aglomerado dessas duas
entidades. Tradicionalmente, usual dizer que o par um cons. As funes car e cdr so meramente os selectores que devolvem o primeiro e o
segundo elemento do par.
Eis um exemplo da criao de um par de nmeros:

170

_$
(1
_$
1
_$
2

(cons 1 2)
. 2)
(car (cons 1 2))
(cdr (cons 1 2))

Notemos que quando o Lisp pretende escrever o resultado da criao


de um par, ele comea por escrever um abrir parnteses, depois escreve o
primeiro elemento do par, depois escreve um ponto para separar, 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. No entanto, quando o segundo elemento do par tambm um par, o
Lisp emprega uma notao mais simples que omite o ponto e um par de
parntesis:
_$ (cons 1 (cons 2 3))
(1 2 . 3)

No exemplo anterior, se o Lisp seguisse apenas a notao de par com


ponto, teria de escrever (1 . (2 . 3)).
Finalmente, quando o segundo elemento do par uma lista vazia, o
Lisp limita-se a escrever o primeiro elemento entre um par de parntesis.
_$ (cons 1 ())
(1)

a combinao destas duas regras que nos permite visualizar sequncia


de pares terminada com uma lista vazia como sendo uma lista:
_$ (cons 1 (cons 2 (cons 3 ())))
(1 2 3)

6.2

Representao Grfica de Pares

Dissemos anteriormente que, ao fazermos (cons ), estamos a criar um


par com os elementos e . Em termos da memria do Lisp, isto implica
que ser reservada memria para conter esse par, que representamos graficamente com uma caixa dividida em duas metades, a mais esquerda a
apontar para o primeiro argumento do cons (o car) e a mais direita a
apontar para o segundo argumento do cons (o cdr). Por exemplo, a expresso (cons 1 "dois") pode-se representar nesta notao grafica, denominada de caixa e ponteiro, tal como se segue:

"dois"
171

Obviamente, um cons pode apontar para outro cons. precisamente isso


que acontece quando criamos listas. Como vimos, uma lista no mais do
que um cons cujo cdr aponta para outra lista (vazia ou no). Por exemplo,
a lista criada pela expresso (list 1 2 3) absolutamente equivalente
lista (cons 1 (cons 2 (cons 3 nil))). A sua representao grfica :
nil
1

Por aqui se v que, do ponto de vista do Lisp, uma lista no mais do


que um particular arranjo de pares em que o segundo elemento de cada par
, ou outro par, ou uma lista vazia.
Assim, a lista vazia () o ponto de partida para a construo de qualquer lista, tal como podemos ver na seguinte sequncia de expresses:
_$ (cons 3 ())
(3)
_$ (cons 2 (cons 3 ()))
(2 3)
_$ (cons 1 (cons 2 (cons 3 ())))
(1 2 3)

Repare-se, nas expresses anteriores, que o segundo argumento da funo cons , ou uma lista vazia, ou uma lista contendo alguns elementos.
Nestes casos, o resultado da invocao da funo cons ser sempre uma
lista.

6.3

Tipos Recursivos

Obviamente, qualquer que seja a lista que imaginemos, sempre possvel


constru-la usando apenas invocaes da funo cons e a lista vazia (). De
facto, temos que (list) equivalente a () e que (list e1 e2 ... en )
equivalente a (cons e1 (list e2 ... en )).
Consequentemente, podemos dizer que uma lista sempre:
ou a lista vazia (),
ou o cons de um elemento a uma lista.
Notemos que h um detalhe subtil na definio de lista que acabmos
de apresentar: ela recursiva! Para o confirmar basta reparar que estamos
a definir uma lista como o cons de um elemento a uma lista, i.e., usamos
o termo que queremos definir na prpria definio. Como j sabemos de
qualquer definio recursiva, necessrio termos um caso de paragem que,
no caso das listas, a lista vazia.
172

Quando um tipo de dados definido de forma recursiva dizemos que


temos um tipo recursivo. As listas so, portanto, um tipo recursivo. Num
tipo recursivo tem de existir sempre um elemento primordial, a partir do
qual se criam os restantes elementos. A esse elemento primordial chama-se
elemento primitivo. No caso das listas, o elemento primitivo , obviamente,
a lista vazia.
Tal como acontecia com os outros tipos de dados, as vrias operaes
que permitem manipular listas podem classificar-se em construtores, selector e reconhecedores. fcil ver que () e cons so construtores, car
e cdr so selectores e, finalmente, null o reconhecedor. Usando estas
operaes podemos agora definir inmeras outras operaes sobre listas.

6.4

Recurso em Listas

Uma das propriedades interessantes dos tipos recursivos que todas as


operaes que processam elementos do tipo tendem a ser implementadas
por funes recursivas. Por exemplo, se quisermos definir uma funo que
nos diz quantos elementos existem numa lista, temos de pensar recursivamente:
Se a lista vazia, ento o nmero de elementos , obviamente, zero.
Caso contrrio, o nmero de elementos ser um mais o nmero de
elementos do resto da lista.
Para nos assegurarmos da correco do nosso raciocnio temos de garantir que se verificam os pressupostos a que todas as definies recursivas
tm de obedecer, nomeadamente:
Que h uma reduo da complexidade do problema a cada invocao
recursiva: de facto, a cada invocao recursiva a lista usada mais
pequena.
Que h um caso mais simples de todos onde existe uma resposta imediata: quando a lista vazia, a resposta zero.
Que existe uma equivalncia entre o problema original e o uso do
resultado da recurso: verdade que o nmero de elementos de uma
lista o mesmo que somar 1 ao nmero de elementos dessa lista sem
o primeiro elemento.
A verificao destes pressupostos suficiente para garantirmos que a
funo est correcta.
Traduzindo este raciocnio para Lisp, obtemos:

173

(defun numero-elementos (lista)


(if (null lista)
0
(+ 1 (numero-elementos (cdr lista)))))

Experimentando, temos:
_$ (numero-elementos (list 1 2 3 4))
4
_$ (numero-elementos (list))
0

importante percebermos que a presena de sublistas no altera o nmero de elementos de uma lista. De facto, os elementos das sublistas no
so considerados elementos da lista. Por exemplo:
_$ (list (list 1 2) (list 3 4 5 6))
((1 2) (3 4 5 6))
_$ (numero-elementos (list (list 1 2) (list 3 4 5 6)))
2

Como se v no exemplo anterior, a lista apenas contm dois elementos,


embora cada um deles seja uma lista com mais elementos.
Na verdade, a funo numero-elementos j existe em Lisp com o
nome length.
Uma outra operao til aquela que nos permite obter o n-simo elemento de uma lista. Vimos que essa funo j existe e se denomina nth mas
agora vamos definir a nossa prpria verso. pragmtica usual assumir-se
que, para n = 0, se deve obter o primeiro elemento da lista. Para definir
esta funo devemos, mais uma vez, pensar de forma recursiva:
Se n zero, devolvemos o primeiro elemento da lista.
Caso contrrio, devolver o n-simo elemento da lista o mesmo que
devolver o (n 1)-simo elemento do resto da lista.
Novamente verificamos que h uma reduo do problema, que a reduo equivalente ao problema original e que o problema se vai aproximando do caso bsico. Em Lisp, temos:
(defun n-esimo (n lista)
(if (= n 0)
(car lista)
(n-esimo (- n 1) (cdr lista))))
_$ (n-esimo 2 (list 0 1 2 3 4))
2

frequentemente necessrio termos de concatenar listas. Para isso, podemos imaginar uma funo concatena que, dadas duas listas, devolve
uma lista contento, pela mesma ordem, os elementos das duas listas. Por
exemplo:
174

_$ (concatena (list 1 2 3) (list 4 5 6 7))


(1 2 3 4 5 6 7)

Como sempre, a recurso ajuda a resolver o problema. Comeemos


por pensar na simplificao do problema, i.e., em transformar o problema
original (concatena (list 1 2 3) (list 4 5 6 7)) num problema ligeiramente mais simples.
Uma vez que h duas listas como parmetros, podemos simplificar o
problema fazendo uma reduo numa delas, i.e., podemos considerar uma
reduo na primeira lista
(concatena (list 2 3) (list 4 5 6 7))

cujo resultado (2 3 4 5 6 7), ou, alternativamente, uma reduo na segunda lista


(concatena (list 1 2 3) (list 5 6 7))

cujo resultado (1 2 3 5 6 7).


Para alm da simplificao do problema, tambm necessrio garantir
que conseguimos arranjar uma maneira de fazer com que a simplificao
do problema seja equivalente ao problema original. Para a primeira alternativa isso trivial, basta inserir o nmero 1 cabea da lista, i.e., fazer
(cons 1 (concatena (list 2 3) (list 4 5 6 7)))

Para a segunda alternativa isso bastante mais difcil pois teriamos de


inserir o nmero 4 algures no meio da lista resultante.
Assim sendo, no devero restar muitas dvidas que a funo dever
ter a forma:
(defun concatena (l1 l2)
(if ???
???
(cons (car l1)
(concatena (cdr l1) l2))))

Falta agora tratar o caso bsico. Para isso, basta pensar que se estamos
a reduzir a primeira lista a cada invocao recursiva ento chegar um momento em que a primeira lista fica vazia. O que ento a concatenao de
uma lista vazia a uma outra lista qualquer? Obviamente, a outra lista.
Assim, podemos finalmente definir completamente a funo:
(defun concatena (l1 l2)
(if (null l1)
l2
(cons (car l1)
(concatena (cdr l1) l2))))

Na verdade, esta funo j existe em Lisp com o nome append e tem a


vantagem adicional de receber qualquer nmero de argumentos, por exemplo:
175

_$ (append (list 0) (list 1 2 3) (list 4 5) (list 6 7 8 9))


(0 1 2 3 4 5 6 7 8 9)

A operao inversa da concatenao de listas a separao de listas.


Para ser mais til, vamos considerar uma funo que obtm uma sublista
a partir de uma lista dada. Essa sublista inclui todos os elementos entre
dois ndices dados. Usualmente, convenciona-se que a sublista inclui o
elemento com o primeiro ndice e exclui o elemento com o segundo ndice.
Como exemplo, temos:
_$ (sublista (list 0 1 2 3 4 5 6) 2 4)
(2 3)

Para definirmos esta funo, podemos recursivamente simplificar o problema da obteno da sublista de (e0 e1 ... en ) entre os ndices i e j para
passar a ser a obteno da sublista de (e1 ... en ) entre os ndices i 1
e j 1. Quando i for zero, ento passamos a transformar o problema da
obteno da sublista de (e0 e1 ... en ) entre os ndices 0 e j para passar
a ser o cons de e0 com a sublista de (e1 ... en ) entre os ndices 0 e j 1.
Traduzindo este algoritmo para Lisp, temos:
(defun sublista (lista inicio fim)
(cond ((> inicio 0)
(sublista (cdr lista) (1- inicio) (1- fim)))
((> fim 0)
(cons (car lista)
(sublista (cdr lista) inicio (1- fim))))
(t
())))
Exercicio 6.1 Defina a funo elimina-n-esimo que recebe um nmero n e uma lista, e
elimina o n-simo elemento da lista. Note que o primeiro elemento da lista corresponde a
n igual a zero.
Por exemplo:
_$ (elimina-n-esimo 2 (list 0 1 2 3 4 5))
(0 1 3 4 5)

Exercicio 6.2 Escreva uma funo muda-n-esimo que recebe um nmero n, uma lista e
um elemento, e substitui o n-simo elemento da lista por aquele elemento. Note que o
primeiro elemento da lista corresponde a n igual a zero.
Por exemplo:
_$ (muda-n-esimo 2 (list 0 1 2 3 4 5) 9)
(0 1 9 3 4 5)
_$ (muda-n-esimo 2 (list "Vou" "para" "Coimbra") "Lisboa")
("Vou" "para" "Lisboa")

Exercicio 6.3 Escreva uma funo que dada uma lista de elementos devolve um elemento
dessa lista, escolhido aleatoriamente.
Exercicio 6.4 Defina a funo um-de-cada que, dada uma lista de listas, constri uma lista
contendo, por ordem, um elemento aleatrio de cada uma das listas. Por exemplo:

176

_$
(2
_$
(1
_$
(1

(um-de-cada (list (list 0 1 2) (list 3 4) (list 5 6 7 8)))


4 7)
(um-de-cada (list (list 0 1 2) (list 3 4) (list 5 6 7 8)))
3 5)
(um-de-cada (list (list 0 1 2) (list 3 4) (list 5 6 7 8)))
4 8)

Exercicio 6.5 Defina a funo elementos-aleatorios que, dado um nmero n e uma


lista de elementos devolve n elementos dessa lista escolhidos aleatoriamente. Por exemplo:
_$ (elementos-aleatorios 3 (list 0 1 2 3 4 5 6))
(1 5 2)

Exercicio 6.6 Redefina a funo elementos-aleatorios de modo a que o resultado respeite a ordem dos elementos na lista de onde foram escolhidos.
Exercicio 6.7 Como vimos, a funo cons acrescenta um novo primeiro elemento a uma
lista, a funo car devolve o primeiro elemento de uma lista e a funo cdr devolve a lista
sem o primeiro elemento.
Escreva as funes inversas do cons, car e cdr, designadas snoc, rac e rdc que,
ao invs de operarem com o primeiro elemento, operam com o ltimo. O snoc recebe um
elemento e uma lista e junta o elemento ao fim da lista. O rac devolve o ltimo elemento
da lista. O rdc devolve uma lista com todos os elementos menos o ltimo.
Exercicio 6.8 Defina uma funo inverte que recebe uma lista e devolve outra lista que
possui os mesmos elementos da primeira s que por ordem inversa.

6.5

Predicados sobre Listas

Para testarmos 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.
Uma vez que as funes car e cdr nos permitem inspecionar dotted
pairs, a sua aplicao consecutiva permite-nos chegar at aos constituintes
ltimos dos pares, que sero nmeros, strings, etc. Estes constituintes ltimos, por j no serem mais decomponveis por intermdio das funes
car e cdr, denominam-se atmicos: em Lisp, tudo o que no for um dotted
pair considerado um tomo.
177

Para facilitar a identificao das entidades atmicas, a linguagem Lisp


disponibiliza uma funo denominada atom que s verdadeira para as
entidades atmicas:
_$ (atom 1)
T
_$ (atom "dois")
T
_$ (atom (cons 1 "dois"))
nil

Uma vez que as listas so constituidas por pares, lgico que no sejam
consideradas tomos. H, no entanto, uma excepo: a lista vazia considerada simultneamente uma lista e um tomo. Isso visivel na seguinte
interaco:
_$ (atom (list 1 "dois" 3.0))
nil
_$ (atom (list))
T
Exercicio 6.9 Sendo as listas uma estrutura de dados to flexvel, muito frequente usarmos listas contendo outras listas que, por sua vez, podem conter outras listas, at ao nvel
de profundidade que se queira. Por exemplo, consideremos a seguinte lista de posies:
_$ (list (list 1 2) (list 3 4) (list 5 6))
((1 2) (3 4) (5 6))

ou a seguinte lista de listas de posies:


_$ (list (list (list 1 2) (list 3 4) (list 5 6))
(list (list 7 8) (list 9 0)))
(((1 2) (3 4) (5 6)) ((7 8) (9 0)))

Escreva uma funo denominada alisa que recebe uma lista (possivelmente com sublistas) como argumento e devolve outra lista com todos os tomos da primeira e pela
mesma ordem, i.e.
_$ (alisa (1 2 (3 4 (5 6)) 7))
(1 2 3 4 5 6 7)

Exercicio 6.10 Escreva uma funo que calcula o nmero de tomos que uma lista (possivelmente com sublistas) tem.
Exercicio 6.11 A concatenao de strings pode ser realizada pela funo strcat. Defina a
funo concatena-strings que, dada uma lista de strings, devolve uma s string com a
concatenao de todas elas. Por exemplo:
_$ (concatena-strings (list "A" "vista" "da" "Baixa" "da" "Banheira"))
"AvistadaBaixadaBanheira"

Exercicio 6.12 A funo concatena-strings cola as palavras umas s outras sem deixar espaos entre elas. Defina uma nova funo denominada forma-frase que recebe
uma lista de strings e concatena-as mas deixando um espao entre cada duas palavras. Por
exemplo:

178

_$ (forma-frase (list "A" "vista" "da" "Baixa" "da" "Banheira"))


"A vista da Baixa da Banheira"

Exercicio 6.13 Defina a funo frase-aleatoria que recebe uma lista de listas de palavras alternativas e devolve uma frase feita pela escolha aleatria de palavras de cada uma
das listas. Por exemplo, sucessivas avaliaes da seguinte expresso
(frase-aleatoria
(list (list "AutoLisp" "Scheme" "Common Lisp")
(list "" "sempre foi" "continua a ser")
(list "uma linguagem")
(list "fantstica." "fabulosa." "moderna.")))

podem produzir os seguintes resultados:


"Common Lisp uma linguagem moderna."
"Common Lisp uma linguagem fantstica."
"Scheme sempre foi uma linguagem moderna."
"AutoLisp continua a ser uma linguagem fantstica."
"AutoLisp uma linguagem moderna."
"Scheme uma linguagem fantstica."
"Scheme continua a ser uma linguagem fantstica."

Sugesto: defina a funo frase-aleatoria empregando as funes forma-frase


e um-de-cada.
Exercicio 6.14 Considere a criao de uma funo para fazer discursos. A ideia bsica
que num discurso dizemos sempre a mesma coisa mas de formas diferentes para no parecermos repetitivos. Eis um exemplo da interaco pretendida:
_$ (discurso-aleatorio)
"Queridos amigos. com grande satisfao que aqui vos revejo."
_$ (discurso-aleatorio)
"Queridos companheiros. com imensa alegria que nesta sala vos reencontro."
_$ (discurso-aleatorio)
"Queridos colegas. com grande satisfao que neste local vos reencontro."
_$ (discurso-aleatorio)
"Queridos colegas. com imensa alegria que nesta sala vos revejo."
_$ (discurso-aleatorio)
"Queridos companheiros. com enorme satisfao que aqui vos reencontro."
_$ (discurso-aleatorio)
"Caros colegas. com grande satisfao que nesta sala vos revejo."
_$ (discurso-aleatorio)
"Estimados companheiros. com grande alegria que neste local vos reencontro."
_$ (discurso-aleatorio)
"Caros amigos. com grande alegria que aqui vos revejo."
_$ (discurso-aleatorio)
"Caros colegas. com imensa alegria que neste local vos revejo."
_$ (discurso-aleatorio)
"Estimados amigos. com imensa satisfao que nesta sala vos revejo."

Estude a interaco apresentada e defina a funo discurso-aleatorio.

6.6

Enumeraes

Vamos agora considerar uma funo que ser til no futuro para criar enumeraes. Dados os limites a e b de um intervalo [a, b] e um incremento i,
a funo dever devolver uma lista com todos os nmeros a, a + i, a + 2i,
a + 3i, a + 4i,. . . , at que a + ni > b.
Mais uma vez, a recurso ajuda: a enumerao dos nmeros no intervalo [a, b] com um incremento i exactamente o mesmo que o nmero a
179

seguido da enumerao dos nmeros no intervalo [a + i, b]. O caso mais


simples de todos uma enumerao num intervalo [a, b] em que a > b.
Neste caso o resultado simplesmente uma lista vazia.
A definio da funo agora trivial:
(defun enumera (a b i)
(if (> a b)
()
(cons a
(enumera (+ a i) b i))))

Como exemplo, temos:


_$
(1
_$
(1

(enumera 1 5 1)
2 3 4 5)
(enumera 1 5 2)
3 5)

Para tornarmos a funo ainda mais genrica, podemos tratar tambm


o caso em que o incremento d negativo. Isto til, por exemplo, para uma
contagem regressiva: (enumera 10 0 -1).
Note-se que a definio actual da funo enumera no permite isso pois
a utilizao de um incremento negativo provoca recurso infinita. claro
que o problema est no facto do teste de paragem ser feito com a funo
> que s apropriada para o caso em que o incremento positivo. No
caso de incremento negativo deveriamos usar <. Assim, para resolvermos
o problema temos de identificar primeiro qual a funo correcta a usar para
a comparao, algo que podemos fazer com um simples teste:
(defun enumera (a b i)
(if ((if (> i 0)
>
<)
a b)
()
(cons a
(enumera (+ a i) b i))))

Agora j podemos ter:


_$
(1
_$
(5
_$
(6

(enumera 1 5 1)
2 3 4 5)
(enumera 5 1 -1)
4 3 2 1)
(enumera 6 0 -2)
4 2 0)

Exercicio 6.15 Infelizmente, a funo enumera desnecessariamente ineficiente pois, apesar do incremento nunca mudar ao longo das invocaes recursivas, estamos sistematicamente a testar se ele positivo. Obviamente, basta testar uma nica vez para se decidir, de
uma vez por todas, qual o teste que se deve fazer. Redefina a funo enumera de modo a
que no sejam feitos quaisquer testes desnecessrios.

180

Exercicio 6.16 A funco (pronuncia-se iota) representa uma enumerao desde 0 at


um limite superior n exclusive, com os elementos da enumerao separados por um dado
incremento, i.e.:
_$
(0
_$
(0

(iota
1 2 3
(iota
2 4 6

10 1)
4 5 6 7 8 9)
10 2)
8)

Defina a funo iota custa da funo enumera.


Exercicio 6.17 A funo enumera pode apresentar um comportamento bizarro quando o
incremento no um nmero inteiro. Por exemplo:
_$
(0
_$
(0

(enumera 0 1 (/ 1.0
0.1 0.2 0.3 0.4 0.5
(enumera 0 1 (/ 1.0
0.01 0.02 0.03 0.04

10))
0.6 0.7 0.8 0.9 1.0)
100))
... 0.94 0.95 0.96 0.97 0.98 0.99)

Como podemos verificar, ao invs de terminar no limite superior 1, a funo termina


1
1
mais cedo. A causa deste comportamento est no facto de as fraces 10
e 100
no serem representveis com exactido em notao binria e, por isso, medida que se soma
uma quantidade suficientemente grande destes nmeros, acumula-se um erro que se vai
tornando significativo.
O algoritmo de Kahan permite minimizar este problema. Pesquise este algoritmo e useo para implementar uma nova verso da funo enumera que evite acumulao de erros
quando o incremento no um nmero inteiro.
Exercicio 6.18 Escreva uma funo membro? que recebe um objecto e uma lista e verifica
se aquele objecto existe na lista.
Exercicio 6.19 Escreva uma funo elimina que recebe, como argumentos, um elemento
e uma lista e devolve, como resultado, outra lista onde esse elemento no aparece.
Exercicio 6.20 Escreva uma funo substitui que recebe dois elementos e uma lista
como argumentos e devolve outra lista com todas as ocorrncias do segundo elemento
substitudas pelo primeiro.
Exercicio 6.21 Escreva uma funo remove-duplicados que recebe uma lista como argumento e devolve outra lista com todos os elementos da primeira mas sem duplicados,
i.e.:
_$ (remove-duplicados (1 2 3 3 2 4 5 4 1))
(3 2 5 4 1)

6.7

Comparao de Listas

Vimos que o Lisp disponibiliza vrias funes de comparao e, em particular, a funo = aplicvel a nmeros, strings e smbolos.
No entanto, quando aplicamos esta funo a pares os resultados no
so os esperados:

181

_$ (= 1 1)
T
_$ (= "dois" "dois")
T
_$ (= (cons 1 "dois") (cons 1 "dois"))
nil

Note-se que embora a funo no tenha dado qualquer erro (implicando que aplicvel a pares), devolveu falso a indicar que os pares so
diferentes. Experimentemos agora uma variante:
_$ (setq par-1-dois (cons 1 "dois"))
(1 . "dois")
_$ (= par-1-dois par-1-dois)
T

Como se pode ver, a comparao foi feita entre duas referncia para um
par criado anteriormente. Supreendentemente, o resultado agora afirmativo. Qual ento a semntica da funo = aplicado a pares? A resposta
subtil: quando aplicada a dois argumentos do tipo par, a funo = devolve
verdade apenas quando os dois argumentos so o mesmo par.
Consideremos, por exemplo, a seguinte interao:
_$
(1
_$
(1
_$
(1

(setq l1 (list 1 2 3))


2 3)
(setq l2 (list 1 2 3))
2 3)
(setq l3 l2)
2 3)

fcil de ver que a avaliao das expresses anteriores cria as seguintes


estruturas em notao de caixa e ponteiro:
l1
1

nil

l2

l3
O aspecto crucial destas estruturas que l2 e l3 se referem mesma
lista. Por este motivo, o operador = considera o valor de l2 igual ao valor
de l3. J o mesmo no acontece quando se compara l2 com l1 pois estes
nomes designam diferentes estruturas. Este comportamento visvel na
seguinte interaco:

182

_$ (= l1 l2)
nil
_$ (= l2 l3)
T

Note-se que o que est em causa nas expresses anteriores apenas se


os valores que so passados como argumentos da funo = so, na realidade, o mesmo valor. irrelevante se esses valores so provenientes da
avaliao de variveis ou da avaliao de quaisquer outras expresses.
Apesar de termos usado o operador = em todas as comparaes que fizemos, pragmaticamente falando, o seu uso costuma estar reservado para
comparaes entre nmeros e strings, utilizando-se um outro operador diferente denominado eq para testar a igualdade de valores. A funo eq
serve precisamente para testar se os seus dois argumentos so, na realidade, o mesmo objecto.47
Exercicio 6.22 Explique a seguinte interaco:
_$ (setq l1 (list 3 4 5))
(3 4 5)
_$ (setq l2 (cons 1 (cons 2 l1)))
(1 2 3 4 5)
_$ (eq l1 l2)
nil
_$ (eq (cdr (cdr l2)) l1)
T

6.8

Comparao de Smbolos

At agora, apenas temos usado smbolos para a construo dos nossos programas, como nomes de funes e variveis. No entanto, os smbolos so
tambm teis noutros contextos em que so usados como valores.
Por exemplo, como vimos na seco 2.29, a funo type devolve um
smbolo que identifica o tipo de objecto. Vimos tambm que, para a definio de um reconhecedor para um determinado tipo, era til poder fazer
comparaes entre smbolos, de modo a identificarmos o smbolo pretendido:
(defun realp (obj)
(= (type obj) real))

Acontece que, em Lisp, os smbolos gozam de uma propriedade muito


importante: so nicos. Isto quer dizer que no existem dois smbolos diferentes com o mesmo nome. Assim, se duas expresses avaliarem para smbolos com o mesmo nome, ento essas expresses tm, como valor, exactamente o mesmo smbolo. Consequentemente, o operador mais adequado
para fazer a comparao , mais uma vez, o eq. Por este motivo, a definio
da funo realp deveria ser, na realidade:
47

Em Auto Lisp, a funo eq consegue ainda comparar nmeros de tipos diferentes,


como 1 e 1.0. Este comportamento, contudo, no usual na famlia de dialectos Lisp.

183

(defun realp (obj)


(eq (type obj) real))

6.9

Comparao Estrutural de Listas

At agora, a comparao de listas est restrita a sabermos se duas listas so,


na realidade, a mesma lista. No entanto, frequentemente ocorre a necessidade de sabermos se duas listas so estruturalmente iguais. A igualdade
estrutural define-se de forma recursiva:
Qualquer objecto estruturalmente igual a si prprio.
Dois cons so estruturalmente iguais se os cars forem estruturalmente
iguais e os cdrs forem estruturalmente iguais.
Esta definio sugere que temos de tratar distintamente os elementos
atmicos dos no-atmicos. Se forem atmicos, ento s sero iguais se
forem eq. Caso contrrio, se no so eq e algum deles atmico, no podem ser iguais. Finalmente, se nenhum deles atmico ento so ambos
no-atmicos e se os cars forem iguais, os cdrs tambm tero de ser. Desta
forma, podemos trivialmente definir a igualdade estrutural com uma funo igual cuja definio :
(defun igual? (obj1 obj2)
(cond ((eq obj1 obj2)
t)
((or (atom obj1) (atom obj2))
nil)
((igual? (car obj1) (car obj2))
(igual? (cdr obj1) (cdr obj2)))))

A linguagem Auto Lisp generaliza ainda esta funo de modo a que a


comparao de entidades numricas possa contemplar uma determinada
margem de erro: dois nmeros so considerados iguais se a sua diferena,
em valor absoluto, inferior a esta margem de erro. A funo equal prdefinida em Auto Lisp recebe, para alm das entidades a comparar, um
terceiro argumento opcional correspondente a esta margem de erro. Notese o seguinte exemplo:
_$ (equal (list
(list
nil
_$ (equal (list
(list
0.05)
T

1 3 (list 3 4))
1.01 2.99 (list 3 4.01)))
1 3 (list 3 4))
1.01 2.99 (list 3 4.01))

184

Carregamento de Ficheiros

medida que vamos definindo funes teis torna-se conveniente poder


contar com elas sempre que necessrio. Para isso, o AutoCad disponibiliza
uma funo que, a partir de um ficheiro contendo todas as definies pretendidas, carrega essas definies para a sua memria. A funo denominase load48 e recebe, como argumento, a string com o caminho para o ficheiro.49
Por exemplo, se o ficheiro C:\AutoLisp\utilitarios.lsp contiver funes que pretendemos ter disponveis no AutoCad, a avaliao da
expresso:
(load "C:\\AutoLisp\\utilitarios.lsp")

ir provocar a avaliao de todas as expresses contidas nesse ficheiro e, no


final, devolve o valor da ltima expresso.
Note-se, na expresso anterior, a utilizao de \\ em vez de \. Como
vimos na secco 2.9, nas strings, o carcter \ um carcter de escape: serve
para incluir na string outros caracteres como, por exemplo, as aspas. Infelizmente, o Windows usa o mesmo carcter como separador de directorias,
pelo que somos obrigados a usar \\. Para simplificar, o Auto Lisp tambm
admite o carcter / como separador de directorias em strings que designam
caminhos para ficheiros. Isto quer dizer que a expresso anterior tambm
poderia ser escrita na forma:
(load "C:/AutoLisp/utilitarios.lsp")

Se, no caminho para o ficheiro, se omitir a extenso deste,50 o Auto Lisp


ir procurar pelo ficheiro mais recente que tenha aquele nome e uma das
extenses .vlx, .fas e .lsp. Mais frente veremos o significado destas
extenses.
Infelizmente, o uso de caminhos completos para os nossos ficheiros
torna os nossos programas dependentes da localizao correcta desses ficheiros. Isto dificulta muito o trabalho de quem quer utilizar os nossos
ficheiros mas usa uma estrutura de directrios diferente da nossa. Para obviar a este problema, o Auto Lisp tem um mecanismo que permite evitar
especificar o caminho para um ficheiro, permitindo-nos escrever apenas:
(load "utilitarios.lsp")

ou, omitindo tambm a extenso:


48
O comando APPLOAD do AutoCad faz basicamente o mesmo mas usa uma interface
grfica para a seleco do ficheiro.
49
A funo recebe ainda mais um argumento, opcional,...
50
A extenso de um ficheiro o conjunto de (usualmente) trs letras que vem a seguir ao
nome do ficheiro e que separado deste por um ponto. A extenso usada normalmente
para identificar o tipo do ficheiro.

185

(load "utilitarios")

Quando omitimos o caminho para o ficheiro, ento o Auto Lisp ir procurar o ficheiro segundo a seguinte estratgia:
1. O ficheiro procurado na directoria actual.51
2. Se no for encontrado, o ficheiro procurado na directoria do desenho actual.
3. Se no for encontrado, o ficheiro pesquisado em cada um dos directrios especificados no caminho de pesquisa do AutoCad.
4. Se no for encontrado, o ficheiro pesquisado na directoria que contm o AutoCad.
Uma vez que no conveniente alterar a directoria actual ou a directoria do desenho actual ou colocar os nossos ficheiros misturados com os do
AutoCad, a melhor soluo para facilmente podermos carregar os nossos
utilitrios consiste em criar uma directoria onde colocamos os vrios ficheiros e acrescentamos o caminho para essa directoria no caminho de pesquisa
do AutoCad. Esse caminho especificado por uma sequncia de directrios indicados no tabulador Files, opo Support File Search Path do
dilogo que resulta do comando OPTIONS.
Note-se que a sequncia de directrios indicados nesse tabulador define
a ordem de pesquisa feita pelo AutoCad. Se pretendemos que os nossos
ficheiros sejam encontrados primeiro que outros com o mesmo nome, ento
deveremos acrescentar o caminho para a nossa directoria antes dos outros
caminhos na sequncia.

7.1

Carregamento de Dependncias

Em geral, cada ficheiro de Lisp que criamos deve indicar explicitamente


quais so as dependncias que tem de outros ficheiros, i.e., quais so os
ficheiros que devem estar previamente carregados para que aquele possa
ser tambm carregado.
Como exemplo, consideremos um ficheiro utils.lsp que define operaes bsicas teis. Com base nestas operaes bsicas crimos vrias
operaes sobre listas que guardmos num ficheiro listas.lsp. Finalmente, temos um terceiro ficheiro grafico.lsp que depende de algumas
das operaes definidas em listas.lsp e ainda de algumas operaes
destinadas a facilitar o desenho de grficos e que se encontram no ficheiro
grafico-utils.lsp.
51

Esta a directoria em que o AutoCad comea a funcionar. No caso do Windows, corresponde ao valor da opo Start In das propriedades do cone do AutoCad.

186

Para que possamos avaliar (load "grafico") e, com isso, carregar


tudo o que preciso, conveniente que cada ficheiro indique as suas dependncias. Assim, o contedo do ficheiro grafico.lsp, dever ser algo
da forma:
grafico.lsp
(load "listas")
(load "grafico-utils")
(defun ...)
...

Naturalmente, o ficheiro listas.lsp ser da forma:


listas.lsp
(load "utils")
(defun ...)
...

Deste modo, quando avaliamos (load "grafico") o Auto Lisp localiza o ficheiro em questo e, uma vez que a primeira expresso desse
ficheiro (load "listas"), o Auto Lisp suspende momentaneamente o
carregamento do ficheiro grafico e procura pelo ficheiro listas. Quando
o encontra, comea a avaliar as suas expresses mas, mais uma vez, como a
primeira dessas expresses (load "utils") o Auto Lisp suspende momentaneamente o carregamento do ficheiro e procura pelo ficheiro utils.
Quando o encontra, avalia todo o seu contedo, regressando ento ao carregamento do ficheiro listas. Aps ter avaliado todas as expresses
deste ficheiro regressa finalmente ao primeiro ficheiro grafico para passar avaliao da expresso seguinte: (load "grafico-utils"). Novamente, o Auto Lisp suspende momentaneamente o carregamento do ficheiro grafico, para agora procurar pelo ficheiro grafico-utils, avaliar todas as suas expresses e, por fim, regressar ao ficheiro grafico para
avaliar as restantes formas.
Esta relao de dependncia entre os vrios ficheiros pode ser representada graficamente num grafo, tal como se apresenta na Figura 56.

7.2

Mdulos

A um conjunto de ficheiros que, juntos, providenciam uma determinada


funcionalidade, chamamos um modulo. Assim, os ficheiros grafico, listas,
utils e grafico-utils constituem um mdulo. O mesmo se poder
dizer do subconjunto constitudo pelos ficheiros listas e utils e, no limite, do subconjunto constitudo pelo ficheiro utils. Assim, seria mais
correcto dizer que existe um mdulo de utilitrios constitudo pelo nico
ficheiro utils, existe um mdulo de listas que depende do mdulo de
187

utils

listas

grafico-utils

grafico
Figura 56: Dependncias entre os ficheiros grafico, listas, utils e
grafico-utils.
utilitrios e que composto pelo ficheiro listas e, finalmente, existe um
mdulo de grficos que depende do mdulo de listas e que constitudo
pelos ficheiros grafico e grafico-utils.
O conceito de mdulo muito importante pois permite abstrair um conjunto de ficheiros numa s entidade. Do ponto de vista de quem usa o mdulo, irrelevante saber se ele constitudo por um, dois ou mil ficheiros,
qual a ordem de carregamento desses ficheiros ou quais as dependncias
que esse mdulo tem de outros mdulos. Desde que no se reduza a funcionalidade de um mdulo, esta abstraco permite que quem criou um
mdulo possa alter-lo, acrescentando, retirando ou modificando ficheiros,
sem afectar quem depende do mdulo.
Consideremos agora uma situao mais complexa (mas mais frequente).
Para alm dos ficheiros grafico-utils.lsp, grafico.lsp, listas.lsp
e utils.lsp que tnhamos no exemplo anterior, temos ainda, num outro
ficheiro chamado testes.lsp, operaes teis para a realizao de testes,
operaes essas cuja definio depende, por um lado, de algumas das operaes de utils e, por outro, de algumas das operaes de listas, tal
como se apresenta em seguida:
testes.lsp
(load "utils")
(load "listas")
(defun ...)
...

Finalmente, temos um ficheiro colunas.lsp contendo operaes para


o desenho de colunas e que depende, quer de testes, quer de utils, quer
de graficos:

188

utils
listas

grafico-utils

grafico

testes

colunas
Figura 57: Dependncias entre os ficheiros grafico, listas, utils e
grafico-utils.
colunas.lsp
(load "testes")
(load "utils")
(load "graficos")
(defun ...)
...

Estas relaes de dependncias esto representadas no grafo presente


na Figura 57.
Se colunas depende de testes e testes depende de utils ento
redundante dizer que colunas depende de utils. De facto, a relao
de dependncia uma relao transitiva, i.e., se x depende de y e y depende
de z, ento automaticamente x depende de z. Isto sugere que, no ficheiro
colunas, deveramos remover a expresso que carrega o ficheiro utils
pois ele j ter sido ser carregado automaticamente em consequncia da
avaliao da expresso anterior (load "testes").
Contudo, existe um pressuposto importante neste raciocnio: para que
colunas.lsp possa dispensar o carregamento de utils, preciso que o
autor de colunas.lsp saiba que utils uma dependncia de testes.
Ora isso implica que temos de conhecer a estrutura interna do mdulo de
testes, o que viola a abstraco desse mdulo. Essa violao tem como
consequncia que quaisquer alteraes que o autor do mdulo de testes
fizer na estrutura do seu mdulo poder ter implicaes negativas para o
mdulo de colunas. Por exemplo, se o autor do mdulo de testes decidir
terminar a dependncia que tinha de utils, ento o mdulo de colunas
ir ter problemas pois o ficheiro utils, afinal, j no carregado.
Por este motivo, sempre prefervel que cada mdulo indique explicitamente as suas dependncias de outros mdulos e no assuma qualquer
conhecimento sobre a estrutura interna ou dependncias desses mdulos.
Infelizmente, preservar a abstraco dos mdulos acarreta um problema:
189

um ficheiro pode acabar por ser carregado mltiplas vezes. Para percebermos isto, pensemos na sequncia de carregamentos que ocorre quando
avaliamos (load "colunas"):
1. (load "testes")
(a) (load "utils")
(b) (load "listas")
i. (load "utils")
2. (load "utils")
3. (load "grafico")
(a) (load "listas")
i. (load "utils")
4. (load "grafico-utils")
fcil ver que, ao preservarmos a abstraco de cada mdulo, i.e., ao
no conhecermos as dependncias de cada mdulo, acabamos inadvertidamente por carregar mltiplas vezes vrios ficheiros: listas carregado
duas vezes e utils carregado quatro vezes.
Obviamente, este mltiplo carregamento de ficheiros indesejvel. Para
resolver este problema, devemos complementar o processo de carregamento
com um registo de tudo o que j tiver sido carregado para evitar carregar
algum ficheiro mais do que uma vez.
Para isso, vamos comear por usar uma varivel global para conter os
nomes dos ficheiros j carregados e, em seguida, definimos uma funo
usa que, dado o nome de um ficheiro que se pretende usar, verifica se ele
j foi carregado e, se no tiver sido, carrega-o. Estas duas formas sero
colocadas no ficheiro usa.lisp:
usa.lsp
(setq ficheiros-carregados ())
(defun usa (nome)
(if (member nome ficheiros-carregados)
nil
(progn
(load nome)
(setq ficheiros-carregados
(cons nome ficheiros-carregados))
t)))

Usando a funo usa no lugar de load, passamos a ter os ficheiros:

190

grafico.lsp
(usa "listas")
(usa "grafico-utils")
(defun ...)
...
listas.lsp
(usa "utils")
(defun ...)
...
testes.lsp
(usa "utils")
(usa "listas")
(defun ...)
...
colunas.lsp
(usa "testes")
(usa "utils")
(usa "graficos")
(defun ...)
...

Agora, ao avaliarmos a expresso (load "colunas") obtemos o seguinte comportamento:


1. (usa "testes") Ainda no foi carregado.
2. (load "testes")
(a) (usa "utils") Ainda no foi carregado.
(b) (load "utils")
(c) (usa "listas") Ainda no foi carregado.
(d) (load "listas")
i. (usa "utils") J foi carregado.
3. (usa "utils") J foi carregado.
4. (usa "grafico") Ainda no foi carregado.
(a) (usa "listas") J foi carregado.
5. (usa "grafico-utils") Ainda no foi carregado.
Como podemos ver, agora cada ficheiro carregado uma nica vez.
191

7.3

Carregamento Automtico

Com a funo uso j temos a possibilidade de carregar os mdulos de que


necessitamos sem nos preocuparmos com carregamentos duplicados. Infelizmente, esta funo s est disponvel depois de a carregarmos, o que
inconveniente pois temos de nos lembrar de carregar manualmente o ficheiro "usa"sempre que iniciamos o trabalho em AutoCad. Para obviar esta
inconvenincia, o AutoCad permite o carregamento automtico de ficheiros
segundo o seguinte esquema de ficheiros:
O ficheiro acad.lsp carregado automaticamente quando o AutoCad inicia.
O ficheiro acaddoc.lsp carregado automaticamente quando abrimos um desenho.
O AutoCad pesquisa por estes ficheiros usando as mesmas regras anunciadas anteriormente para localizar ficheiros sem a indicao do caminho.
Este esquema permite-nos ter um ficheiro de inicializao que carrega
tudo aquilo que entendemos ser til para a nossa sesso de trabalho. Se
for til independentemente do desenho em questo, dever ser definido
ou carregado no ficheiro acad.lsp. Se for til para um s desenho, ento
o melhor ter um ficheiro acaddoc.lsp contendo tudo o que for preciso
e guardado na directoria desse desenho. Desta forma, diferentes desenhos
(guardados em diferentes directorias) podem ter diferentes ficheiros de inicializao acaddoc.lsp.
H ainda outros ficheiros que o AutoCad carrega automaticamente mas
que esto relacionados com outros recursos do AutoCad e no com o Auto
Lisp.
Tendo esta informao em conta, deveremos criar um ficheiro acad.lsp
numa das directorias presentes no caminho de pesquisa do AutoCad e,
nesse ficheiro, devermos incluir a seguinte expresso:
acad.lsp
(load "usa")

7.4

Interpretao e Compilao

Vimos que, quando o Auto Lisp avalia uma expresso, ele primeiro analisaa, decompondo-a nas suas subexpresses e, recursivamente, avalia estas de
acordo com as regras da linguagem, seguindo este processo at chegar s
expresses mais simples de todas, para as quais o Auto Lisp sabe imediatamente qual o valor, passando ento a combinar os valores das subavaliaes at produzir o valor da expresso original.
Este processo, que o Auto Lisp segue para a avaliao de qualquer expresso, denomina-se interpretao e o programa que o realiza diz-se um
192

interpretador. Quando estamos a usar o REPL do AutoCad estamos, na realidade, a usar um interpretador para a linguagem Auto Lisp.
Embora a interpretao seja um processo muito til para a depurao
dos programas, sofre de um problema: computacionalmente pouco eficiente. Se pensarmos, por exemplo, na funo factorial, fcil vermos
que a avaliao de uma invocao da funo factorial implica uma sequncia de anlises e combinao de resultados que tem de ser repetida para
cada uma das sucessivas invocaes recursivas da funo.
Existe um outro processo de avaliao que, embora menos flexvel que
a interpretao, tem o potencial para ser muito mais eficiente: a compilao.
A compilao uma operao que realizada por um compilador e que
se baseia em analisar expresses sem as avaliar e em transform-las noutras expresses equivalentes que, quando avaliadas, produzam os mesmos
resultados mas de forma mais eficiente. A ideia fundamental da compilao que possvel converter um programa escrito numa linguagem num
programa escrito noutra linguagem que seja mais eficiente de interpretar
ou, no limite, que seja directamente executvel pelo computador.
Em geral, a compilao em Lisp transforma um ficheiro contendo expresses Lisp num outro ficheiro com as mesmas expresses traduzidas
para outra linguagem muito mais eficiente de avaliar. Em geral, h duas
vantagens em usar ficheiros compilados:
muito mais rpido carregar um ficheiro compilado do que um no
compilado.
A invocao das funes que foram definidas a partir de um ficheiro
compilado mais rpida, por vezes, muito mais rpida.
Para se compilar um ficheiro necessrio invocar a funo vlisp-compile.
Esta funo recebe trs argumentos:
1. Um smbolo indicando qual o grau de optimizao desejado.
2. Uma string indicando o ficheiro a compilar. Se a extenso do ficheiro
for omissa, considera-se .lsp como extenso. Se apenas se indicar o nome do ficheiro, sem o caminho at ele, o Visual Lisp usa o
processo normal de procura de ficheiros para o encontrar.
3. Uma string opcional indicando o caminho para o ficheiro a criar para
conter o resultado da compilao. Quando omisso, idntico ao argumento anterior, mas usando .fas como extenso. Quando no
omisso, ento poder ser apenas o nome do ficheiro (caso em que o
o Visual Lisp ir colocar o ficheiro na sua directoria de instalao) ou
o caminho completo para o ficheiro.

193

Em relao ao nvel de optimizao, h trs possibilidades (indicadas


pelos smbolos st, lsm e lsa) mas apenas a primeira preserva (quase) todas as caractersticas da linguagem Lisp. As seguintes alteram ligeiramente
a semntica da linguagem para conseguirem obter maior performance. Por
exemplo, para l do nvel st, a redefinio de funes deixa de ter efeito
e a informao de depurao cada vez menor, tornando os programas
mais eficientes mas dificultando a vida ao programador quando ele tenta
perceber qualquer erro que tenha ocorrido.52
Como exemplo de utilizao, a seguinte expresso mostra a compilao
do ficheiro listas.lsp:53
(vlisp-compile st "listas")

Listas de Coordenadas

As listas constituem um tipo de dados extraordinariamente til. Atravs


da utilizao de listas possvel escrever programas mais simples, mais
claros e mais genricos.
Vimos anteriormente que sempre que pretendiamos indicar no AutoCad uma posio geomtrica no plano bidimensional usvamos os construtores de coordenadas Cartesianas ou polares que, por sua vez, criavam lista
com as coordenadas correspondentes, contendo dois elementos no caso bidimensional e trs elementos no caso tri-dimensional.
Uma outra utilizao frequente de listas ocorre quando se pretende
agrupar um conjunto de entidades geomtricas. Neste caso, contrariamente
ao que acontecia com a utilizao de listas para representar posies 2D ou
3D, a lista usualmente no possui um nmero fixo de elementos.

8.1

Polgonos

Imaginemos, a ttulo de exemplo, que pretendemos representar um polgono. Por definio, um polgono uma figura plana limitada por um caminho fechado composto por uma sequncia de segmentos de recta. Cada
segmento de recta uma aresta (ou lado) do polgono. Cada ponto onde se
encontram dois segmentos de recta um vrtice do polgono.
A partir da definio de poligono fcil vermos que uma das formas
mais simples de representar um polgono ser atravs de uma sequncia
de coordenadas que indica qual a ordem pela qual devemos unir os vrtices com uma aresta e onde se admite que o ltimo elemento ser unido
ao primeiro. Para representar esta sequncia podemos simplesmente usar
52

Essa dificuldade de compreenso pode ser til quando se pretende ceder a terceiros um
programa mas em que no se pretende que se saiba como est feito.
53
Note-se a utilizao da plica para evitar a avaliao do smbolo st.

194

uma lista. Por exemplo, uma lista com as coordenadas de quatro vrtices pode representar um quadriltero, um octgono ser representado por
uma lista de oito vrtices, um hentriacontgono ser representado por uma
lista de trinta e um vrtices, etc.
A representao de polgonos usando listas apenas o primeiro passo
para os podermos visualizar graficamente. O segundo passo a invocao
dos comandos do AutoCad que constroem o polgono em questo. Para
isso, temos de invocar o comando line (ou, alternativamente, o comando
pline) e, de seguida, passar os sucessivos pontos para o AutoCad. No
final da sequencia de pontos temos ainda de passar a opo close para
que o AutoCad feche o polgono, unindo o ltimo ao primeiro ponto.
Decompondo o problema em duas partes, podemos escrever:
(defun poligono (vertices)
(command "_.line")
(passa-pontos vertices)
(command "_close"))

Uma vez que a passagem dos pontos tem de ser feita percorrendo a lista
com os vrtices, vamos definir uma funo recursiva para o fazer:
(defun passa-pontos (pontos)
(if (null pontos)
nil
(progn
(command (car pontos))
(passa-pontos (cdr pontos)))))

Usando a funo poligono podemos agora facilmente desenhar inmeros polgonos. Por exemplo, as seguintes avaliaes produzem o resultado apresentado na Figura 58.
(poligono (list (xy -2 -1) (xy 0 2) (xy 2 -1)))
(poligono (list (xy -2 1) (xy 0 -2) (xy 2 1)))

importante notarmos que a funo poligono independente de quaisquer polgonos em particular. Ela tanto se aplica ao desenho de tringulos como quadrados como qualquer outro polgono. Esta independncia
tornou-se possvel pela utilizao de listas para representar as coordenadas
dos vrtices dos polgonos. Agora, apenas necessrio concentrarmo-nos
na criao destas listas de coordenadas.
8.1.1

Estrelas Regulares

O polgono representado na Figura 58 foi gerado manualmente pela sobreposio de dois tringulos. Um polgono mais interessante o famoso

195

Figura 58: Polgonos em AutoCad.


pentagrama, ou estrela de cinco pontas, que tem sido usado com conotaes simblicas, mgicas ou decorativas desde os tempos da Babilnia.54
A Figura 59 demonstra o uso do pentagrama (bem como do octograma,
do octgono e do hexgono) como elemento decorativo e estrutural numa
janela de pedra.
parte as conotaes extra-geomtricas, o pentagrama , antes de tudo,
um polgono. Por este motivo, desenhvel pela funo poligono desde
que consigamos produzir uma lista com as coordenadas dos vrtices. Para
isso, vamos reportar-nos Figura 60 onde visvel que os cinco vrtices do
pentagrama dividem o crculo em 5 partes, com arcos de 2
5 cada. Dado
o centro do pentagrama, o seu vrtice superior faz um ngulo de 2 com o
eixo das abcissas. Esse vrtice deve ser unido, no com o vrtice seguinte,
mas sim com o imediatamente a seguir a esse, i.e., aps uma rotaco de
dois arcos ou 22
= 4
5
5 = 0.8. Estamos agora em condies de definir
a funo vertices-pentagrama que constri a lista com os vrtices do
pentagrama:
54
O pentagrama foi smbolo de perfeio matemtica para os Pitagricos, foi smbolo
do domnio do esprito sobre os quatro elementos da matria pelos ocultistas, representou as cinco chagas de Cristo crucificado para os Cristos, foi associado s propores do
corpo humano, foi usado como smbolo manico e, quando invertido, at foi associado ao
satanismo.

196

Figura 59: Variaes de Estrelas numa janela do Forte de Amber, localizado


no estado de Jaipur, na ndia. Fotografia de David Emmett Cooley.

2
5

Figura 60: Construo do Pentagrama.

197

(defun vertices-pentagrama (centro


(list (+pol centro raio (+ (/ pi
(+pol centro raio (+ (/ pi
(+pol centro raio (+ (/ pi
(+pol centro raio (+ (/ pi
(+pol centro raio (+ (/ pi

raio)
2) (*
2) (*
2) (*
2) (*
2) (*

0
1
2
3
4

0.8
0.8
0.8
0.8
0.8

pi)))
pi)))
pi)))
pi)))
pi)))))

(poligono (vertices-pentagrama (xy 0 0) 1))

Como bvio, a funo vertices-pentagrama possui excessiva repetio de cdigo, pelo que seria prefervel encontrarmos uma forma mais
estruturada de gerarmos aqueles vrtices. Para isso, vamos comear por
pensar na generalizao da funo.
O caso geral de um pentagrama a estrela regular, em que se faz variar o
nmero de vrtices e o nmero de arcos que separam um vrtice do vrtice
a que ele se une. O pentagrama um caso particular da estrela regular em
que o nmero de vrtices cinco e o nmero de arcos de separao dois.
Matematicamente falando, uma estrela regular representa-se pelo smbolo
de Schlfli55 { av } em que v o nmero de vrtices e a o nmero de arcos
de separao. Nesta notao, um pentagrama escreve-se como { 52 }.
Para desenharmos estrelas regulares vamos idealizar uma funo que,
dado o centro da estrela, o raio do crculo circunscrito, o nmero de vrtices
v (a que chamaremos n-vertices) e o nmero de arcos de separao a (a
que chamaremos n-arcos), calcula o tamanho do arco que preciso
avanar a partir de cada vrtice. Esse arco , obviamente, = a 2
v . Tal
como no pentagrama, para primeiro vrtice vamos considerar um ngulo
inicial de = 2 . A partir desse primeiro vrtice no preciso mais do
que ir aumentando o ngulo de de cada vez. As seguintes funes
implementam este raciocnio:
(defun vertices-estrela (p raio n-vertices n-arcos)
(pontos-circulo p
raio
pi/2
(* n-arcos (/ 2*pi n-vertices))
n-vertices))
(defun pontos-circulo (p raio fi dfi n)
(if (= n 0)
(list)
(cons (+pol p raio fi)
(pontos-circulo p
raio
(+ fi dfi)
dfi
(- n 1)))))

Com a funo vertices-estrela agora trivial gerar os vrtices de


55

Ludwig Schlfli foi um matemtico e gemetra Susso que fez importantes contribuies, em particular, na geometria multidimensional.

198

Figura 61: Estrelas regulares em AutoCad. Da esquerda para a direita, temos um pentagrama ({ 52 }), dois heptagramas ({ 27 } e { 73 }) e um octograma
({ 83 }).
qualquer estrela regular. A Figura 61 apresenta as estrelas regulares desenhadas a partir das seguintes expresses:
(poligono (vertices-estrela (xy 0 0) 1 5 2))
(poligono (vertices-estrela (xy 2 0) 1 7 2))
(poligono (vertices-estrela (xy 4 0) 1 7 3))
(poligono (vertices-estrela (xy 6 0) 1 8 3))

muito importante salientarmos a forma como a representao grfica de estrelas est separada em duas partes distintas. De um lado, o da
funo vertices-estrela, produzimos as coordenadas dos vrtices das
estrelas pela ordem em que estes devem ser unidos. No outro lado, o da
funo poligono, usamos essas coordenadas para criar uma representao grfica dessa estrela baseada em linhas que ligam os seus vrtices. A
passagem das coordenadas de um lado para o outro realizada atravs de
uma lista que produzida num lado e consumida no outro.
Esta utilizao de listas para separar diferentes processos fundamental
e ser por ns repetidamente explorada para simplificar os nossos programas.
8.1.2

Polgonos Regulares

Um polgono regular um polgono que tem todos os lados de igual comprimento e todos os ngulos de igual amplitude. A Figura 62 ilustra exemplos
de polgonos regulares. Como bvio, um polgono regular um caso particular de uma estrela regular em que o nmero de arcos de separao
um.
Para criarmos polgonos regulares, vamos definir uma funo denominada vertices-poligono-regular que gera uma lista de coordenadas
correspondente s posies dos vrtices de um polgono regular de n lados, inscrito numa circunferncia de raio r centrada em p, cujo primeiro
vrtice faz um ngulo com o eixo dos X. Sendo um polgono regular um

199

Figura 62: Polgonos regulares. Da esquerda para a direita temos um tringulo equiltero, um quadrado, um pentgono regular, um hexgono regular, um heptgono regular, um octgono regular, um enegono regular e
um decgono regular.
caso particular de uma estrela regular, basta-nos invocar a funo que computa os vrtices de uma estrela regular mas usando, para o parmetro ,
apenas a diviso da circunferncia 2 pelo nmero de lados n, i.e.:
(defun vertices-poligono-regular (p r fi n)
(pontos-circulo p r fi (/ 2*pi n) n))

Finalmente, podemos encapsular a gerao dos vrtices com o seu uso


para criar o poligono correspondente no AutoCad:
(defun poligono-regular (p r fi n)
(poligono
(vertices-poligono-regular p r fi n)))

8.2

Iterao em Listas

At agora temos processado as listas usando apenas recurso. Sendo a lista


um tipo de dados recursivo natural que a forma mais simples de computar valores a partir de listas seja atravs de funes recursivas.
No entanto, existem casos, como o da funo passa-pontos, em que
o nico resultado relevante da recurso o conjunto de efeitos secundrios
que vo sendo realizados medida que a recurso se vai desenrolando. De
facto, se observarmos com ateno a definio da referida funo constatamos que ocorre a seguinte reduo:
(passa-pontos l)

(passa-pontos (cdr l))


Contrariamente ao que acontecia nos casos tradicionais de recurso,
neste nada computado com o resultado da invocao recursiva, ou seja, a
invocao recursiva nada deixa pendente. Esta forma de recurso denominase iterao.
Para estes casos em que apenas interessam os efeitos secundrios do
processamento dos vrios elementos de uma lista o Auto Lisp providencia uma forma que simplifica substancialmente os programas: a forma
foreach. A sua sintaxe a seguinte:
200

Figura 63: Estrelas regulares { pr } com p = 20 e r a variar desde 1 ( esquerda) at 9 ( direita).


(foreach nome expresso
expresso1
.
.
.
expresson )

A forma foreach recebe o nome de uma varivel, uma expresso que


deve avaliar para uma lista e vrias expresses que sero avaliadas para
cada elemento da lista, com o nome associado a esse elemento. Por exemplo, a seguinte expresso escreve todos os elementos de uma lista:
(foreach numero (list 1 2 3 4 5)
(print numero))

Usando a forma foreach agora mais fcil definir a funo passa-pontos:


(defun passa-pontos (pontos)
(foreach ponto pontos
(command ponto)))

Na realidade, a funo ficou to simples que prefervel elimin-la e


juntar os vrios comandos na funo poligono:
(defun poligono (vertices)
(command "_.line")
(foreach vertice vertices
(command vertice))
(command "_close"))

A iterao em listas tambm muito til para experimentarmos variaes de figuras geomtricas. Uma vez que a funo enumera (definida na
seco 6.6) gera uma sequncia de nmeros, podemos usar esses nmeros
em combinao com a forma foreach para desenhar uma sequncia de
figuras. Por exemplo, a seguinte expresso gera a Figura 63:
(foreach a (enumera 1 9 1)
(poligono
(vertices-estrela (xy (* 2 a) 0) 1 20 a)))

Exercicio 8.1 Utilizando o operador foreach, defina a funo denominada rectangulo-envolvente


que, dada uma lista de coordenadas bi-dimensionais, devolve uma lista com as coordenadas dos cantos inferior esquerdo e superior direito do menor rectngulo capaz de incluir
todas as coordenadas dadas. Por exemplo,

201

_$ (rectangulo-envolvente (list (xy 0 0) (xy 3 4) (xy -1 0) (xy 2 -3)))


((-1 -3) (3 4))

Exercicio 8.2 Considere um polgono representado pela lista dos seus vrtices (p0 p1 ... pn ),
tal como apresentamos no seguinte esquema, esquerda:
P2

P2

P1

P3

P1

P3
P0

P4

P0

P4

Pretende-se dividir o polgono em dois sub-polgonos, tal como apresentado no esquema anterior, direita. Defina a funo divide-poligono que, dada a lista de vrtices
do polgono e dados dois ndices i e j onde se deve fazer a diviso (com i < j), calcula as listas de vrtices de cada um dos sub-polgonos resultantes e devolve-as numa lista. Por exemplo, no caso da imagem, temos que (divide-poligono (list p0 p1 p2 p3 p4 ) 1 4)
produz a lista de listas ((p0 p1 p4 ) (p1 p2 p3 p4 ))
Exercicio 8.3 Podemos considerar uma generalizao do exerccio anterior baseada na bisseco do polgono usando uma linha arbitrria, tal como podemos ver no esquema seguinte:
P2
P3

P2

P1
P3
P0

P4

P1

P0

P4

Para simplificar o processo, considere que cada extremidade da linha de bisseco est
localizada a uma determinada fraco fi [0, 1] da distncia que vai do vrtice Pi ao vrtice
sequinte Pi+1 (com Pn+1 = P0 ). Por exemplo, no esquema anterior, os vrtices em questo
so P1 e P4 e a fraces so, respectivamente, de f1 = 0.3 e f4 = 0.5.
Defina a funo bisseccao-poligono que, dada a lista de vrtices do polgono e
dados os dois ndices i e j imediatamente anteriores aos extremos da linha de bisseco e
dadas as fraces fi e fj da distncia, respectivamente, entre os vrtices (Pi , Pi+1 ) e entre
(Pj , Pj+1 ), calcula as listas de vrtices de cada um dos sub-polgonos resultantes e devolveas numa lista (i.e., devolve uma lista de listas de vrtices).
Exercicio 8.4 Defina a funo bisseccao-aleatoria-poligono que, dados os vrtices
de um polgono, devolve uma lista de listas de vrtices dos sub-polgonos correspondentes
a uma diviso aleatria do polgono. A seguinte imagem mostra exemplos de divises
aleatrias de um octgono.

202

Exercicio 8.5 Usando a funo bisseccao-aleatoria-poligono definida no exerccio


anterior, defina a funo divisao-aleatoria-poligono que, de forma aleatria, divide
e subdivide recursivamente um poligono at que se atinja um determinado nvel de recurso. A seguinte imagem mostra a diviso aleatria de um decgono para sucessvos nveis
de recurso desde 0 at 10.

8.3

Linhas Poligonais e Splines

Vimos que as listas permitem armazenar um nmero varivel de elementos


e vimos como possvel usar listas para separar os programas que definem
as coordenadas das figuras geomtricas daqueles que usam essas coordenadas para as representarem graficamente.
No caso da funo poligono discutida na seco 8.1, as listas de coordenadas so usadas para a criao de polgonos, i.e., figuras planas limitadas por um caminho fechado composto por uma sequncia de segmentos
de recta. Acontece que nem sempre queremos que as nossas figuras sejam limitadas por caminhos fechados, ou que esses caminhos sejam uma
sequncia de segmentos de recta ou, sequer, que as figuras sejam planas.
Para resolver este problema, necessitamos de definir funes capazes de,
a partir de listas de coordenadas, criarem outros tipos de figuras geomtricas. Assim, vamos deixar de falar em vrtices e vamos passar a falar
genericamente em coordenadas dos pontos que definem as figuras geomtricas.
O caso mais simples o de uma linha poligonal (potencialmente aberta).
J vimos que o comando line permite criar linhas poligonais compostas
por segmentos de recta independentes. Outra alternativa ser no ter segmentos independentes mas sim uma nica entidade geomtrica. Para isso,
podemos usar o comando pline:
(defun pline-pontos (pontos)
(command "_.pline")
(foreach p pontos (command p))
(command ""))

Note-se que no usmos a opo "close" para no fecharmos a linha


poligonal. Convm salientar que o comando pline, embora seja mais eficiente que o comando line, no consegue criar linhas poligonais que no
sejam paralelas ao plano XY . No caso geral em que pretendemos linhas
poligonais tridimensionais, este comando no pode ser usado e teremos de
passar para uma variante menos eficiente mas mais verstil: o comando
3dpoly. Para isso, vamos definir a funo 3dpoly-pontos de modo a
usar linhas poligonais tridimensionais:

203

Figura 64: Comparao entre uma linha poligonal e uma spline que unem o
mesmo conjunto de pontos.
(defun 3dpoly-pontos (pontos)
(command "_.3dpoly")
(foreach p pontos (command p))
(command ""))

No caso de no pretendermos linhas poligonais mas sim curvas mais


suaves podemos empregar uma spline que passe pelos pontos dados,
atravs do comando spline do AutoCad:
(defun spline-pontos (pontos)
(command "_.spline")
(foreach p pontos (command p))
(command "" "" ""))

A diferena entre uma linha poligonal (produzida por line, pline,


ou 3dpoly) e uma spline visvel na Figura 64, onde comparamos uma
sequncia de pontos unida com uma linha poligonal com a mesma sequncia unida com uma spline. O grfico foi produzido pela avaliao das seguintes expresses:
(setq pontos
(list (xy 0 2) (xy 1 4) (xy 2 0) (xy 3 3)
(xy 5 0) (xy 5 4) (xy 6 0) (xy 7 2)
(xy 8 1) (xy 9 4)))
(pline-pontos pontos)
(spline-pontos pontos)

Naturalmente, a especificao manual das coordenadas dos pontos


pouco conveniente, sendo prefervel que essas coordenadas sejam computadas automaticamente de acordo com uma especificao matemtica da
204

curva pretendida. Imaginemos, por exemplo, que pretendemos traar uma


curva sinuside a partir de um ponto P . Infelizmente, de entre as figuras
geomtricas disponibilizadas pelo AutoCadpontos, linhas rectas, rectngulos, polgonos, crculos, arcos de crculo, elipses, arcos de elipse, donuts,
linhas poligonais e splinesno consta a sinuside.
A alternativa criarmos uma aproximao a uma sinuside. Para isso,
podemos calcular uma sequncia de pontos pertencentes curva da sinuside e traar rectas ou, ainda melhor, curvas que passem por esses pontos. Para calcular o conjunto de valores da funo seno no intervalo [x0 , x1 ]
basta-nos considerar um incremento x e, comeando no ponto x0 e passando do ponto xi para o ponto xi+1 atravs de xi+1 = xi + x , vamos
sucessivamente calculando o valor da expresso sin(xi ) at que xi exceda
x1 . Para obtermos uma definio recursiva para este problema podemos
pensar que quando x0 > x1 o resultado ser uma lista vazia de coordenadas, caso contrrio juntamos a coordenada (x0 , sin(x0 )) lista de coordenadas do seno para o intervalo [x0 + x , x1 ]. Esta definio traduzida
directamente para a seguinte funo:
(defun pontos-seno (x0 x1 dx)
(if (> x0 x1)
(list)
(cons (xy x0 (sin x0))
(pontos-seno (+ x0 dx) x1 dx))))

Para termos uma maior liberdade de posicionamento da curva sinusise no espao, podemos modificar a funo anterior para incorporar um
ponto P em relao ao qual se calcula a curva:
(defun pontos-seno (p x0 x1 dx)
(if (> x0 x1)
(list)
(cons (+xy p x0 (sin x0))
(pontos-seno p (+ x0 dx) x1 dx))))

A Figura 65 mostra as curvas traadas pelas seguintes expresses que


unem os pontos por intermdio de plines:
(pline-pontos (pontos-seno (xy 0.0 1.0) 0.0 6.0 1.0))
(pline-pontos (pontos-seno (xy 0.0 0.5) 0.0 6.5 0.5))
(pline-pontos (pontos-seno (xy 0.0 0.0) 0.0 6.4 0.2))

Note-se, na Figura 65, que demos um deslocamento vertical s curvas


para melhor se perceber a diferena de preciso entre elas. Como plenamente evidente, quantos mais pontos se usarem para calcular a curva, mais
proxima ser a linha poligonal da verdadeira curva. No entanto h tambm
que ter em conta que o aumento do nmero de pontos obriga o AutoCad a
maior esforo computacional.
205

Figura 65: Senos desenhados usando plines com um nmero crescente de


pontos.

Figura 66: Senos desenhados usando splines com um nmero crescente de


pontos.
Para se obterem ainda melhores aproximaes, embora custa de ainda
maior esforo computacional, podemos usar splines, simplesmente mudando
as expresses anteriores para usarem a funo spline-pontos no lugar
de pline-pontos. O resultado est visvel na Figura 66.
Exercicio 8.6 Tal como as linhas poligonais podem dar lugar a polgonos, tambm as splines
podem formar curvas fechadas. Para isso, basta indicar a opo close aps o ltimo ponto
fornecido ao comando spline. Defina a funo spline-fechada-pontos que recebe
uma lista de coordenadas e cria uma spline fechada que passa por esses pontos:
Exercicio 8.7 Defina a funo pontos-sinusoide-circular, de parmetros p, ri , re , c
e n que computa n pontos de uma curva fechada com a forma de uma sinusoide com c
ciclos que se desenvolve num anel circular centrado no ponto p e delimitado pelos raios
interior ri e exterior re , tal como se pode ver nos vrios exemplos apresentados na seguinte
figura onde, da esquerda para a direita, o nmero de ciclos c 12, 6, e 3.

206

Exercicio 8.8 Defina a funo pontos-circulo-raio-aleatorio, de parmetros p, r0 ,


r1 e n que computa n pontos de uma curva fechada de forma aleatria contida num anel circular centrado no ponto p e delimitado pelos raios interior ri e exterior re , tal como se pode
ver nos vrios exemplos apresentados na seguinte figura onde, da esquerda para a direita,
fomos aumentando progressivamente o nmero de pontos usados, assim aumentando a
irregularidade da curva.

Sugesto: para a computao dos pontos, considere a utilizao de coordenadas polares


para distribuir uniformemente os pontos em torno de um crculo mas com a distncia ao
centro desse crculo a variar aleatoriamente entre ri e re . A ttulo de exemplo, considere
que a a curva mais esquerda na figura anterior foi gerada pela expresso
(spline-fechada-pontos
(pontos-circulo-raio-aleatorio
(xy 0 0) 1 2 6))

8.4

Trelias

Uma trelia uma estrutura composta por barras rgidas que se unem em
ns, formando unidades triangulares. Sendo o tringulo o nico polgono
intrinsecamente estvel, a utilizao de tringulos convenientemente interligados permite que as trelias sejam estruturas indeformveis. Apesar
da simplicidade dos elementos triangulares, diferentes arranjos destes elementos permitem diferentes tipos de trelias.
conhecido o uso de trelias desde a Grcia antiga, em que eram utilizadas para o suporte dos telhados. No sculo dezasseis, nos seus Quatro
Livros de Arquitectura, Andrea Palladio ilustra pontes de trelias. No sculo
dezanove, com o uso extensivo de metal e a necessidade de ultrapassar
vos cada vez maiores, inventaram-se vrios tipos de trelias que se distinguem pelos diferentes arranjos de barras verticais, horizontais e diagonais
e que, frequentemente, se denominam de acordo com os seus inventores.
Temos assim trelias Pratt, trelias Howe, trelias Town, trelias Warren,

207

Figura 67: A esfera geodsica de Buckminster Fller. Fotografia de Glen


Fraser.
etc. Nas ltimas dcadas as trelias comearam a ser intensivamente utilizadas como elemento artstico ou para a construo de superfcies elaboradas. No conjunto de exemplos mais famosos incluem-se a esfera geodsica
de Buckminster Fller para a Exposio Universal de 1967, apresentada
(reconstruda) na Figura 67 e as trelias em forma de banana de Nicolas
Grimshaw para o terminal de Waterloo, visveis na Figura 68.
As trelias apresentam um conjunto de propriedades que as tornam
particularmente interessantes do ponto de vista arquitectnico:
possvel construir trelias muito grandes a partir de elementos relativamente pequenos, facilitando a produo, transporte e ereco.
Desde que as cargas sejam aplicadas apenas nos ns da trelia, as
barras ficam apenas sujeitas a foras axiais, i.e., trabalham apenas
traco ou compresso, permitindo formas estruturais de grande
eficincia.
Uma vez que os elementos bsicos de construo so barras e ns,
fcil adaptar as dimenses destes s cargas previstas, permitindo
assim grande flexibilidade.
8.4.1

Desenho de Trelias

O passo fundamental para o desenho de trelias a construo dos elementos triangulares fundamentais. Embora frequentemente se conside208

Figura 68: Trelias em forma de banana para o terminal de Waterloo, por


Nicolas Grimshaw. Fotografia de Thomas Hayes.

Figura 69: Trelia composta por elementos triangulares iguais.


rem apenas trelias bi-dimensionais (tambm chamadas trelias planas), iremos tratar o caso geral de uma trelia tri-dimensional composta por semioctaedros. Esta forma de trelia denomina-se de space frame. Cada semioctaedro denominado de mdulo.
A Figura 69 apresenta o esquema de uma trelia. Embora nesta figura
os ns da trelia estejam igualmente espaados ao longo de rectas paralelas,
nada obriga a que assim seja. A Figura 70 mostra uma outra trelia em que
tal no se verifica.
Assim, para o desenho de uma trelia, vamos considerar, como base de

Figura 70: Trelia cujos elementos triangulares no so idnticos entre si.

209

a0
c0

b0

a1
c1

b1

a2
c2

Figura 71: Esquema de ligao de barras de uma trelia em space frame.


trabalho, trs sequncias arbitrrias de pontos em que cada ponto define
um n da trelia. A partir destas trs sequncias podemos criar as ligaes
que necessrio estabelecer entre cada par de ns. A Figura 72 apresenta o
esquema de ligao a partir de trs sequncias de pontos (a0 , a1 , a2 ), (b0 , b1 )
e (c0 , c1 , c2 ). Note-se que a sequncia de topo estabelecida pelos pontos bi
da sequncia intermdia tem sempre menos um elemento que as sequncias ai e ci .
Para a construo da trelia precisamos de encontrar um processo que,
a partir das listas de pontos ai , bi e ci , no s crie os ns correspondentes
aos vrios pontos, como os interligue da forma correcta. Comecemos por
tratar da criao dos ns:
(defun nos-trelica (ps)
(if (null ps)
nil
(progn
(no-trelica (car ps))
(nos-trelica (cdr ps)))))

ou, empregando o operador foreach,


(defun nos-trelica (ps)
(foreach p ps
(no-trelica p)))

A funo no-trelica (notemos o singular, por oposio ao plural empregue na funo nos-trelica) recebe as coordenadas de um ponto e
responsvel por criar o modelo tridimensional que representa o n da trelia centrado nesse ponto. Uma hiptese simples ser esta funo criar uma
esfera onde se encaixaro as barras mas, por agora, vamos deixar a deciso
sobre qual o modelo em concreto para mais tarde e vamos simplesmente
admitir que a funo no-trelica far algo apropriado para criar o n.
Assim, podemos comear a idealizar a funo que constri a trelia completa a partir das listas de pontos as, bs e cs:
(defun trelica
(nos-trelica
(nos-trelica
(nos-trelica
...)

(as bs cs)
as)
bs)
cs)

De seguida, vamos tratar de estabelecer as barras entre os ns. Da an210

lise da Figura 72 ficamos a saber que temos uma ligao entre cada ai e cada
ci , outra entre ai e bi , outra entre ci e bi , outra entre bi e ai+1 , outra entre bi
e ci+1 , outra entre ai e ai+1 , outra entre bi e bi+1 e, finalmente, outra entre ci
e ci+1 . Admitindo que a funo barra-trelica cria o modelo tridimensional dessa barra (por exemplo, um cilindro, ou uma barra prismtica),
podemos comear por definir uma funo denominada barras-trelica
(notemos o plural) que, dadas duas listas de pontos ps e qs, cria barras
de ligao ao longo dos sucessivos pares de pontos. Para criar uma barra,
a funo necessita de um elemento dos ps e outro dos qs, o que implica
que a funo deve terminar assim que uma destas listas estiver vazia. A
definio fica ento:
(defun barras-trelica (ps qs)
(if (or (null ps) (null qs))
nil
(progn
(barra-trelica (car ps) (car qs))
(barras-trelica (cdr ps) (cdr qs)))))

Para interligar cada n ai ao correspondente n ci , apenas temos de


avaliar (barras-trelica as cs). O mesmo poderemos dizer para interligar cada n bi ao n ai correspondente e para interligar cada bi a cada
ci . Assim, temos:
(defun trelica (as bs cs)
(nos-trelica as)
(nos-trelica bs)
(nos-trelica cs)
(barras-trelica as cs)
(barras-trelica bs as)
(barras-trelica bs cs)
...)

Para ligar os ns bi aos ns ai+1 podemos simplesmente subtrair o primeiro n da lista as e estabelecer a ligao como anteriormente. O mesmo
podemos fazer para ligar cada bi a cada ci+1 . Finalmente, para ligar cada
ai a cada ai+1 podemos usar a mesma ideia mas aplicando-a apenas lista
as. O mesmo podemos fazer para a lista cs. A funo completa fica, ento:

211

Figura 72: Trelia construda a partir de pontos especificados arbitrariamente.


(defun trelica (as bs cs)
(nos-trelica as)
(nos-trelica bs)
(nos-trelica cs)
(barras-trelica as cs)
(barras-trelica bs as)
(barras-trelica bs cs)
(barras-trelica bs (cdr
(barras-trelica bs (cdr
(barras-trelica as (cdr
(barras-trelica cs (cdr
(barras-trelica bs (cdr

as))
cs))
as))
cs))
bs)))

As funes anteriores constroem trelias com base nas funes elementares no-trelica e barra-trelica. Embora o seu significado seja
bvio, ainda no definimos estas funes e existem vrias possibilidades.
Numa primeira abordagem, vamos considerar que cada n da trelia ser
constitudo por uma esfera onde se iro unir as barras, barras essas que
sero definidas por cilindros. O raio das esferas e da base dos cilindros
ser determinado por uma varivel global, para que possamos facilmente
alterar o seu valor. Assim, temos:
(setq raio-no-trelica 0.1)
(defun no-trelica (p)
(esfera p raio-no-trelica))
(setq raio-barra-trelica 0.03)
(defun barra-trelica (p0 p1)
(cilindro p0 raio-barra-trelica p1))

Podemos agora criar as trelias com as formas que entendermos. A


Figura 72 mostra uma trelia desenhada a partir da expresso:
212

(trelica
(list (xyz
(xyz
(list (xyz
(xyz
(list (xyz
(xyz

0 -1 0) (xyz 1 -1.1 0) (xyz 2 -1.4 0) (xyz 3 -1.6


4 -1.5 0) (xyz 5 -1.3 0) (xyz 6 -1.1 0) (xyz 7 -1
0.5 0 0.5) (xyz 1.5 0 1) (xyz 2.5 0 1.5) (xyz 3.5
4.5 0 1.5) (xyz 5.5 0 1.1) (xyz 6.5 0 0.8))
0 +1 0) (xyz 1 +1.1 0) (xyz 2 +1.4 0) (xyz 3 +1.6
4 +1.5 0) (xyz 5 +1.3 0) (xyz 6 +1.1 0) (xyz 7 +1

0)
0))
0 2)
0)
0)))

Exercicio 8.9 Defina uma funo denominada trelica-recta capaz de construir qualquer uma das trelias que se apresentam na imagem seguinte.

Para simplificar, considere que as trelias se desenvolvem segundo o eixo X. A funo


trelica-recta dever receber o ponto inicial da trelia, a altura e largura da trelia e o
nmero de ns das fileiras laterais. Com esses valores, a funo dever produzir trs listas
de coordenadas que passar como argumentos funo trelica. Como exemplo, considere que as trs trelias apresentadas na imagem anterior foram o resultado da avaliao
das expresses:
(trelica-recta (xyz 0 0 0) 1.0 1.0 20)
(trelica-recta (xyz 0 5 0) 2.0 1.0 20)
(trelica-recta (xyz 0 10 0) 1.0 2.0 10)

Sugesto: comece por definir a funo coordenadas-linha que, dado um ponto


inicial p, um afastamento l entre pontos e um nmero n de pontos, devolve uma lista com
as coordenadas dos n pontos dispostos ao longo do eixo X.
Exercicio 8.10 O custo total de uma trelia muito dependente do nmero de diferentes
comprimentos que as barras podem ter: quanto menor for esse nmero, maiores economias
de escala se conseguem obter e, consequentemente, mais econmica fica a trelia. O caso
ideal aquele em que existe um nico comprimento igual para todas as barras.
Atendendo ao seguinte esquema, determine a altura h da trelia em funo da largura
l do mdulo de modo a que todas as barras tenham o mesmo comprimento.

213

bi

l
h
ai
ai+1
ci
ci+1
l

Defina ainda a funo trelica-modulo que constri uma trelia com barras todas do
mesmo comprimento, orientada segundo o eixo X. A funo dever receber o ponto inicial
da trelia, a largura da trelia e o nmero de ns das fileiras laterais.
Exercicio 8.11 Dadas as coordenadas dos quatro vrtices da base de uma pirmide quadrangular e a altura dessa pirmide, possvel calcular as coordenadas do vrtice do topo
dessa pirmide determinando o centro da base e o vector normal base.
Exercicio 8.12 Considere o desenho de uma trelia plana, tal como se apresenta na seguinte
figura:
b0

a0

b1

a1

bn1

...

...

an1

an

Defina uma funo trelica-plana que recebe, como parmetros, duas listas de pontos
correspondentes aos pontos desde a0 at an e desde b0 at bn1 e que cria os ns nesses
pontos e as barras que os unem. Considere, como pr-definidas, as funes nos-trelica,
que recebe uma lista de pontos como argumento e barras-trelica que recebe duas listas
de pontos como argumentos.
Teste a funo de definir com a seguinte expresso:
(trelica-plana
(coordenadas-linha (xyz 0 0 0) 2.0 20)
(coordenadas-linha (xyz 1 0 1) 2.0 19))

Exercicio 8.13 Considere o desenho da trelia especial apresentada na seguinte figura:

214

c0
a0
c1
a1

cn
an

b0

bn1
Defina uma funo trelica-especial que recebe, como parmetros, trs listas de
pontos correspondentes aos pontos desde a0 at an , desde b0 at bn1 e desde c0 at cn e que
cria os ns nesses pontos e as barras que os unem. Considere, como pr-definidas, as funes nos-trelica que recebe uma lista de ponto como argumento e barras-trelica
que recebe duas listas de pontos como argumentos.

8.4.2

Gerao de Coordenadas

Como vimos na seco anterior, podemos idealizar um processo de criao


de uma trelia a partir das listas de coordenadas dos seus ns. Estas listas,
naturalmente, podem ser especificadas manualmente mas esta abordagem
s ser realizvel para trelias muito pequenas. Ora sendo uma trelia uma
estrutura capaz de vencer vos muito grandes, no caso geral, o nmero de
ns da trelia demasiado elevado para que possamos produzir manualmente as suas listas de coordenadas. Para resolver este problema, temos de
pensar em processos automatizados para criar essas listas, processos esses
que tenham em conta a geometria pretendida para a trelia.
A ttulo de exemplo, idealizemos um processo de criao de trelias em
arco, em que as sequncias de ns ai , bi e ci formam arcos de circunferncia.
A Figura 73 mostra uma verso de uma destas trelias, definida pelos arcos
de circunferncia de raio r0 e r1 .
Para tornar a trelia uniforme, os ns encontram-se igualmente espaados ao longo do arco. O ngulo corresponde a esse espaamento e
calcula-se trivialmente pela diviso da amplitude angular do arco pelo nmero de ns pretendidos n. Atendendo a que o arco intermdio tem sempre
menos um n do que os arcos laterais, temos de dividir o ngulo pelas
duas extremidades do arco intermdio, de modo a centrar os ns desse arco
entre os ns dos arcos laterais, tal como se pode verificar na Figura 73.
Uma vez que o arco circular, a forma mais simples de calcularmos as
posies dos ns ser empregando coordenadas esfricas (, , ). Esta deciso leva-nos a considerar que os ngulos inicial e final dos arcos devem
ser medidos relativamente ao eixo Z, tal como visvel na Figura 73. Para
215

r1
r0

Figura 73: Alado frontal de uma trelia em forma de arco de crculo.


flexibilizar a produo das coordenadas dos ns do arco vamos definir uma
funo que recebe o centro P do arco, o raio r desse arco, o ngulo , os ngulos inicial 0 e final 1 e, finalmente, o incremento de ngulo . Assim,
temos:
(defun pontos-arco (p r fi psi0 psi1 dpsi)
(if (> psi0 psi1)
(list)
(cons (+esf p r fi psi0)
(pontos-arco p r fi (+ psi0 dpsi) psi1 dpsi))))

Para construirmos a trelia em arco podemos agora definir uma funo


que cria trs dos arcos anteriores. Para isso, a funo ter de receber o
centro P do arco central, o raio rac dos arcos laterais, o raio rb do arco
central, o ngulo , os ngulos inicial 0 e final 1 e, ainda, a separao e
entre os arcos laterais e o nmero n de ns dos arcos laterais. A funo ir
0
calcular o incremento = 1
e, de seguida, invoca a funo trelica
n
com os parmetros apropriados:

216

Figura 74: Trelias em arco criadas com parmetros diferentes.


(defun trelica-arco (p rac rb fi psi0 psi1 e n / dpsi)
(setq dpsi (/ (- psi1 psi0) n))
(trelica
(pontos-arco (+pol p (/ e 2.0) (+ fi pi/2))
rac
fi
psi0 psi1
dpsi)
(pontos-arco p
rb
fi
(+ psi0 (/ dpsi 2.0)) (- psi1 (/ dpsi 2.0))
dpsi)
(pontos-arco (+pol p (/ e 2.0) (- fi pi/2))
rac
fi
psi0 psi1
dpsi)))

A Figura 74 mostra as trelias construdas a partir das expresses:


(trelica-arco (xyz 0 0 0) 10 9 0 -pi/2 pi/2 1.0 20)
(trelica-arco (xyz 0 5 0) 8 9 0 -pi/3 pi/3 2.0 20)

Exercicio 8.14 Considere a construo de abbabas apoiadas em trelias distribuidas radialmente, tal como a que se apresenta na imagem seguinte:

217

Esta abbada constituda por um determinado nmero de trelias de arco circular. A


largura l de cada trelia e o ngulo inicial 0 a que se d o arranque de cada trelia so tais
que os ns dos topos das trelias so coincidentes dois a dois e esto dispostos ao longo de
um crculo de raio r, tal como se apresenta no esquema seguinte:
z

0
r0

x
y

r
x

218

Defina a funo abobada-trelicas que constri uma abbada de trelicas a partir do


centro da abbada P , do raio rac dos arcos laterais de cada trelia, do raio rb do arco central
de cada trelia, do raio r do fecho das trelias, do nmero de ns n em cada trelia e,
finalmente, do nmero de trelias n .
A ttulo de exemplo, considere a figura seguinte que foi produzida pela avaliao das
seguintes expresses:
(abobada-trelicas (xyz 0 0 0) 10 9 2.0 10 3)
(abobada-trelicas (xyz 25 0 0) 10 9 2.0 10 6)
(abobada-trelicas (xyz 50 0 0) 10 9 2.0 10 9)

8.4.3

Trelias Espaciais

Vimos como possvel definir trelias a partir de trs listas cada uma contendo as coordenadas dos ns a que as barras das trelias se ligam. Ligando
entre si vrias destas trelias possvel produzir uma estrutura ainda maior
a que se d o nome de trelia espacial. A Figura 75 mostra um exemplo onde
so visveis trs trelias espaciais.
Para podermos definir um algoritmo que gere trelias espaciais importante termos em conta que embora este tipo de trelias aglomere vrias
trelias simples, estas esto interligadas de tal modo que cada trelia partilha um conjunto de ns e barras com a trelia que lhe adjacente, tal como
visvel na Figura 76 que apresenta um esquema de uma trelia espacial.
Assim, se uma trelia espacial for constituda por duas trelias simples interligadas, a trelia espacial ser gerada, no por seis listas de coordenadas,
mas apenas por cinco listas de coordenadas. No caso geral, uma trelia espacial constituda por n trelias simples, ser definida por um 2n + 1 listas
de pontos, i.e., por um nmero mpar de listas de pontos (no mnimo, trs
listas).
A definio da funo que contri uma trelia espacial segue a mesma
lgica da funo que constri uma trelia simples s que agora, em vez de
operar com apenas trs listas, opera com um nmero impar delas. Assim,
a partir de uma lista contendo um nmero mpar de listas de coordenadas,
iremos processar essas listas de coordenadas duas a duas, sabendo que a
terceira lista de coordenadas ci,j da trelia i tambm a primeira lista
de coordenadas ai+1,j da trelia seguinte i + 1.

219

Figura 75: Trelias espaciais no estdio Al Ain nos Emiratos rabes Unidos.
Fotografia de Klaus Knebel.

b0,0

c0,0 = a1,0

c1,0 = a2,0

b0,1

a0,0

a0,1

b1,0

b1,1
c0,2 = a1,2

c0,1 = a1,1

c1,1 = a2,1

a0,2

c1,2 = a2,2

Figura 76: Esquema de ligao de barras de uma trelia espacial.

220

Uma vez que processamos duas listas de cada vez e partimos de um


nmero mpar de listas, no caso final restar apenas uma lista de coordenadas que dever fechar a construo da trelia.
H, no entanto, uma dificuldade adicional: para que a trelia plana tenha rigidez transversal ainda necessrio interligar entre si os ns centrais
das vrias trelias. Estes travamentos correspondem a ligar cada n bi,j ao
n bi+1,j . Assim, medida que formos processando as listas de coordenadas, iremos tambm estabelecer os travamentos entre as listas correspondentes. Todo este processo implementado pela seguinte funo:
(defun trelica-espacial (curvas / as bs cs)
(setq as (car curvas)
bs (cadr curvas)
cs (caddr curvas))
(nos-trelica as)
(nos-trelica bs)
(barras-trelica as cs)
(barras-trelica bs as)
(barras-trelica bs cs)
(barras-trelica bs (cdr as))
(barras-trelica bs (cdr cs))
(barras-trelica (cdr as) as)
(barras-trelica (cdr bs) bs)
(if (null (cdddr curvas))
(progn
(nos-trelica cs)
(barras-trelica (cdr cs) cs))
(progn
(barras-trelica bs (cadddr curvas))
(trelica-espacial (cddr curvas)))))
Exercicio 8.15 Na realidade, uma trelia simples um caso particular de uma trelia espacial. Redefina a funo trelia de modo a que ela use a funo trelica-espacial.

Agora que j sabemos construir trelias espaciais a partir de uma lista


de listas de coordenadas, podemos pensar em mecanismos para gerar esta
lista de listas. Um exemplo simples o de uma trelia espacial horizontal,
tal como a que se apresenta na Figura 77.
Para gerar as coordenadas dos ns desta trelia, podemos definir uma
funo que, com base no nmero de pirmides pretendidas e na largura
da base da pirmide, gera os ns ao longo de uma das dimenses, por
exemplo, a dimenso X:
(defun coordenadas-linha (p l n)
(if (= n 0)
(list p)
(cons p
(coordenadas-linha (+x p l) l (- n 1)))))

Em seguida, basta-nos definir uma outra funo que itera a anterior ao


longo da outra dimenso Y , de modo a gerar uma linha de ns ai , seguida
221

Figura 77: Uma trelia espacial horizontal, composta por oito trelias simples com dez pirmides cada uma.
de outra linha bi deslocada para o centro da pirmide e altura desta, seguida das restantes linhas, at o final, em que teremos de produzir mais
uma linha ai . ainda necessrio termos em conta que as linhas bi tm menos um n do que as linhas ai . Com base nestas consideraes, podemos
escrever:
(defun coordenadas-piramides-horizontais (p h l m n)
(if (= m 0)
(list (coordenadas-linha p l n))
(cons
(coordenadas-linha p l n)
(cons
(coordenadas-linha
(+xyz p (/ l 2.0) (/ l 2.0) h) l (- n 1))
(coordenadas-piramides-horizontais
(+y p l) h l (- m 1) n)))))

Podemos agora combinar a lista de listas de coordenadas produzidas


pela funo anterior com a que constri uma trelia espacial. A ttulo de
exemplo, a seguinte expresso produz a trelia apresentada na Figura 77:
(trelica-espacial
(coordenadas-piramides-horizontais (xyz 0 0 0) 1 1 8 10))
Exercicio 8.16 Considere a construo de uma trelia espacial aleatria. Esta trelia caracterizase por as coordenadas dos seus ns estarem posicionados a uma distncia aleatria das
coordenadas dos ns correspondentes de uma trelia espacial horizontal, tal como exemplificado pela seguinte figura onde, para facilitar a visualizao, se assinalou a trao mais
grosso as barras que unem os ns ai , bi e ci do esquema apresentado na Figura 76.

222

Defina a funo coordenadas-trelica-aleatoria que, para alm dos parmetros


da funo coordenadas-piramides-horizontais, recebe ainda a distncia mxima r
a que cada n da trelia aleatria pode ser colocado relativamente ao n correspondente
da trelia horizontal. A ttulo de exemplo, considere que a trelia apresentada na figura
anterior foi gerada pela avaliao da expresso:
(trelica-espacial
(coordenadas-trelica-aleatoria (xyz 0 0 0) 1 1 8 10 0.2))

Exercicio 8.17 Considere a construo de uma trelia espacial em arco, tal como a que se
apresenta na imagem seguinte (em perspectiva). Defina a funo trelica-espacial-arco
que, para alm dos parmetros da funo trelica-arco, possui ainda um parmetro adicional que indica o nmero de trelias simples que constituem a trelia espacial. A ttulo de
exemplo, considere que a trelia apresentada na imagem seguinte foi gerada pela expresso:
(trelica-espacial-arco (xyz 0 0 0) 10 9 1.0 20 0 -pi/3 pi/3 10)

Exercicio 8.18 Considere a trelia apresentada na imagem seguinte:

223

Esta trelia semelhante trelia espacial em arco mas com uma nuance: os raios exterior rac e interior rb variam ao longo do eixo do arco. Esta variao corresponde a uma
sinusoide de amplitude r a variar desde um valor inicial 0 at um valor final 1 , em
incrementos de .
Defina a funo trelica-ondulada que constri este tipo de trelias, a partir dos
mesmos parmetros da funo trelica-espacial-arco e ainda dos parmetros 0 , 1 ,
e r . Como exemplo, considere que a figura anterior foi gerada pela invocao seguinte:
(trelica-ondulada (xyz 0 0 0) 10 9 1.0 20 0 -pi/3 pi/3 0 4*pi (/ pi 8) 1)

Manipulao de Entidades

O AutoCad no s um programa de desenho. Ele tambm uma base de


dados sobre figuras geomtricas. De facto, sempre que desenhamos algo o
AutoCad regista na sua base de dados a entidade grfica criada, bem como
algumas informaes adicionais relacionadas com essa entidade como, por
exemplo, a sua cor.
Existem vrias maneiras de se aceder s entidades criadas. Uma das
mais simples para um utilizador normal do AutoCad ser atravs do
uso do rato, simplesmente clicando na entidade grfica a que pretendemos aceder. Outra, mais til para quem pretende programar, ser atravs
da invocao de funes do Auto Lisp que devolvem, como resultados, as
entidades geomtricas criadas. H vrias destas funes nossa disposio mas, por agora, vamos limitar-nos a uma das mais simples: a funo
entlast.
A funo entlast no recebe quaisquer argumentos e devolve a ltima entidade criada no AutoCad. Para alm de registar todas as entidades
224

geomtricas criadas durante uma sesso de desenho, o AutoCad mantm


ainda um registo (que vai sendo continuamente actualizado) da ltima dessas entidades, de modo a que o programador a ela possa aceder simplesmente invocando a funo entlast. Se ainda no tiver sido criada nenhuma entidade, a funo devolve nil.
A seguinte interaco demonstra o comportamento desta funo aps a
criao de um crculo:
_$ (command "_.circle" (xy 10 20) 30)
nil
_$ (entlast)
<Entity name: 7ef91ed8>

O valor retornado pela avaliao de (entlast) tem, como representao externa, o texto <Entity name: ???>, em que ??? um identificador
da entidade, destinado apenas a permitir ao leitor distinguir diferentes entidades entre si pois cada entidade ter necessariamente um identificador
diferente.
Dada uma entidade, podemos estar interessados em conhecer as suas
propriedades. Para isso, o Auto Lisp disponibiliza a funo entget. Esta
funo recebe uma entidade como argumento e devolve uma lista com as
propriedades da entidade. Eis um exemplo:56
_$ (entget (entlast))
((-1 . <Entity name: 7ef91ed8>)
(0 . "CIRCLE")
(330 . <Entity name: 7ef91db8>)
(5 . "5B")
(100 . "AcDbEntity")
(67 . 0)
(410 . "Model")
(8 . "0")
(100 . "AcDbCircle")
(10 10.0 20.0 0.0)
(40 . 30.0)
(210 0.0 0.0 1.0))

Como podemos ver, o valor devolvido uma lista de pares. Isto


vlido mesmo para os ltimo e o antepenltimo elementos da lista, i.e.,
(210 0.0 0.0 1.0) e (10 10.0 20.0 0.0), que no aparentam ter a
forma de pares simplesmente porque o segundo elemento de cada um desses pares , ele prprio uma lista. Como j vimos aquando da relao entre
pares e listas, aqueles elementos, na realidade, correspondem, respectivamente, aos pares (210 . (0.0 0.0 1.0)) e (10 . (10.0 20.0 0.0)).
56

Indentmos o resultado da avaliao para melhor se compreender o valor devolvido.

225

Cdigo
-1
0
1
2
3-4
5
6
7
8
10
11-18
38
39
48
50-58
60
62
100
210

Significado
Nome (uma string que pode mudar de sesso para sesso)
Tipo
Texto primrio
Nome dado pelo utilizador (presente apenas nalgumas entidades)
Outros textos (presente apenas nalgumas entidades)
Referncia(uma string que nunca muda)
Tipo de linha (presente apenas nalgumas entidades)
Estilo de texto (presente apenas nalgumas entidades)
Nome do layer
Ponto primrio (o centro para um crculo, ponto inicial para
uma linha, etc)
Outros pontos (presente apenas nalgumas entidades)
Elevao (apenas para entidades com elevao diferente de
zero)
Espessura (apenas para entidades com espessura diferente de
zero)
Escala do tipo de linha (presente apenas nalgumas entidades)
ngulos
Visibilidade (opcional, 0 para visvel, 1 para invisvel)
ndice da cor
Marcador de subclasse
Direco de extruso
Tabela 5: Alguns dos cdigos DXF

9.1

Listas de Associaes

Este tipo de lista cujos elementos so pares denomina-se uma lista de associaes ou, abreviadamente, uma assoc-list ou, ainda mais abreviadamente,
uma alist.
No resultado devolvido pela ltima expresso h alguns elementos facilmente relacionveis com o crculo que tnhamos criado, nomeadamente,
a string "CIRCLE", as coordenadas (10.0 20.0 0.0) e o raio 30. Menos
claro o significado dos nmeros que aparecem como primeiro elemento
de cada par. Esses nmeros designam, na realidade, uma codificao numrica da propriedade correspondente e encontram-se definidos pela especificao DXF (Drawing eXchange Format) inventada pela AutoDesk para
permitir troca de informao entre aplicaes de CAD. Assim, o nmero 0
designa o tipo da entidade, o nmero 10 designa o ponto primrio da entidade (o centro do crculo, neste caso), etc. A Tabela 5 descreve o significado
de alguns dos cdigos definidos pela especificao DXF.
226

Quando se pretende saber apenas uma das propriedades de uma entidade, por exemplo, o seu tipo, pouco prtico ter de a procurar visualmente numa lista razoavelmente grande como a que resulta de entget.
Note-se que no basta aceder posio onde julgamos estar a propriedade
pois essa posio no a mesma para todos os objectos (pois nem todos
os objectos possuem as mesmas propriedades) nem necessariamente a
mesma para diferentes verses do AutoCad. Naturalmente, sendo o resultado da funo entget uma lista de pares, nada nos impede de definir
uma funo que processe essa lista procura da propriedade em questo.
Para concretizarmos, consideremos que pretendemos definir uma funo que, a partir do cdigo DXF de uma propriedade que procuramos e
da lista de propriedades de uma entidade, nos diz qual o seu valor para
aquela entidade. Para isso, o mais simples aplicarmos recurso:
Se o primeiro elemento do primeiro par da lista tem o cdigo DXF
que procuramos ento o valor correspondente o segundo elemento
desse par.
Se o primeiro elemento do primeiro par da lista no tem o cdigo
DXF pretendido, ento podemos recursivamente procurar no resto
da lista.
Traduzindo para Auto Lisp, temos:
(defun valor-propriedade (propriedade lista-assoc)
(cond ((= propriedade (car (car lista-assoc)))
(cdr (car lista-assoc)))
(t
(valor-propriedade propriedade (cdr lista-assoc)))))

Um pequeno teste assegura-nos que a funo tem o comportamento


desejado:
_$ (valor-propriedade 0 (entget (entlast)))
"CIRCLE"

A funo valor-propriedade, infelizmente, contm um problema:


tal como se pode ver na Tabela 5, nem todas as propriedades existem para
todas as entidades. Isto implica que possvel a funo valor-propriedade
ser invocada para procurar uma propriedade que no existe numa dada
lista de associaes. Neste caso, a funo teria um comportamento incorrecto pois, uma vez que as invocaes recursivas vo operando sobre uma
lista de propriedades sucessivamente mais pequena, chegar o momento
em que essa lista fica vazia. Como a funo no detecta essa situao continuar a tentar aceder ao primeiro elemento da lista, numa altura em que
a lista j no tem quaisquer elementos. Para resolver este problema basta
227

detectarmos este caso e, caso ocorra, devolver um valor que signifique que
a propriedade no existe naquela lista. Em geral, a linguagem Lisp convenciona que, para essas situaes, o valor a devolver nil. Tendo em conta
este raciocnio, a funo fica ento com a seguinte forma:
(defun valor-propriedade (propriedade lista-assoc)
(cond ((null lista-assoc)
nil)
((= propriedade (car (car lista-assoc)))
(cdr (car lista-assoc)))
(t
(valor-propriedade propriedade (cdr lista-assoc)))))
_$ (valor-propriedade 0 (entget (entlast)))
"CIRCLE"
_$ (valor-propriedade 10 (entget (entlast)))
(10.0 20.0 0.0)
_$ (valor-propriedade 123456789 (entget (entlast)))
nil

9.2

A funo assoc

A funo valor-propriedade to til que at j se encontra pr-definida


em Auto Lisp com o nome assoc mas com uma pequena diferena destinada a facilitar a distino entre no existir uma propriedade numa lista
de associaes e existir essa propriedade mas com o valor nil: ao invs
de devolver o valor da propriedade, a funo assoc devolve o par propriedade/valor, quando existe, ou nil quando no existe. Como um par
necessariamente diferente de nil, ento possvel distinguir os dois casos.
O reverso da moeda que, para aceder ao valor da propriedade torna-se
ainda necessrio usar a funo cdr. Este comportamento visvel no seguinte exemplo:
_$ (assoc 0 (entget (entlast)))
(0 . "CIRCLE")
_$ (cdr (assoc 0 (entget (entlast))))
"CIRCLE"

Finalmente, podemos usar esta funo como base para a construo de


selectores que, ao invs de operarem com a lista de propriedades de uma
entidade, operam sobre a prpria entidade:
(defun tipo-entidade (entidade)
(cdr (assoc 0 (entget entidade))))
(defun ponto-primario (entidade)
(cdr (assoc 10 (entget entidade))))
_$ (tipo-entidade (entlast))
"CIRCLE"
_$ (ponto-primario (entlast))
(10.0 20.0 0.0)

228

9.3

Modificao de Entidades

Vimos como aceder s propriedades de uma entidade. Vamos agora discutir a forma de modificarmos essas propriedades.
A funo entmod permite a modificao de entidades. Para isso, a
funo recebe como argumento uma lista de associaes contendo, para
alm dos pares propriedade/valor que pretendemos modificar, um par
nome/entidade contendo a entidade cujas propriedades pretendemos modificar. Por exemplo, se pretendermos mudar a origem do crculo criado
anteriormente para as coordenadas (0, 0, 0) basta-nos avaliar a expresso:
_$ (entmod (list (cons -1 (entlast))
(cons 10 (xyz 0 0 0))))
((-1 . <Entity name: 7ef91f30>) (10 0 0 0))

Quando bem sucedida, a invocao da funo entmod devolve a lista


de associaes usada. Se ocorrer algum erro, a funo devolve nil.
Como bvio no exemplo anterior, o uso da funo entmod pouco
claro, sendo prefervel criarmos um modificador que esconda o seu uso.
(defun muda-ponto-primario (entidade novo-ponto-primario)
(entmod (list (cons -1 entidade)
(cons 10 novo-ponto-primario))))

Naturalmente, cada modificador deve corresponder a um selector. Por


exemplo, para aceder e modificar a cr de um objecto, podemos definir as
funes:
(defun cor (entidade)
(cdr (assoc 62 (entget entidade))))
(defun muda-cor (entidade nova-cor)
(entmod (list (cons -1 entidade)
(cons 62 nova-cor))))

De igual modo, para aceder e modificar o raio de um crculo, podemos


usar:
(defun raio (entidade)
(cdr (assoc 40 (entget entidade))))
(defun muda-raio (entidade novo-raio)
(entmod (list (cons -1 entidade)
(cons 40 novo-raio))))

9.4

Criao de Entidades

H duas maneiras fundamentais de se criarem entidades em Auto Lisp:57


57

Na realidade, h trs maneiras fundamentais de se criarem objectos em AutoCad mas,


por agora, apenas vamos lidar com duas.

229

Atravs da pseudo-funo command. Esta funo recebe como argumentos as expresses necessrias que sero avaliadas apenas se e
quando o AutoCad precisar dos valores correspondentes para criar a
entidade.
A criao da entidade ainda afectada por um conjunto de parmetros globais, como o layer em que se est a trabalhar, o UCS actual, o
modo OSNAP, a lngua, etc. Para se usarem valores diferentes destes
parmetros necessrio fazer as correspondentes alteraes previamente criao da entidade ou necessrio fazer posteriores alteraes entidade.
O uso desta funo ainda dependente das alteraes que a AutoDesk vai fazendo sintaxe e parmetros dos comandos em sucessivas
verses do AutoCad.
Atravs da funo entmake. Esta funo recebe uma lista de associaes idntica que devolvida pela funo entget e usa os valores
associados s propriedades correspondentes para criar e inicializar a
entidade. As propriedades no obrigatrias ficam com valores por
omisso.
A criao da entidade no afectada por quaisquer outros parmetros. Em particular, no existe dependncia do UCS actual, o que
implica que as coordenadas das entidades criadas so interpretadas
em termos do WCS.
O uso desta funo ainda dependente das alteraes que a AutoDesk vai fazendo ao formato DXF em sucessivas verses do AutoCad.
H ainda mais uma pequena diferena entre estas duas funes: em geral, usar entmake computacionalmente mais eficiente (por vezes, muito
mais eficiente) que usar command, embora esta ltima possa ser mais simples para o programador.
Uma vez que j vimos no passado a pseudo-funo command vamos
agora discutir a entmake. Para exemplificar, consideremos a criao de
um crculo de centro em (4, 4), raio 10 e cr 3 (verde). Como referimos
anteriormente, temos de usar como argumento uma lista de associaes
com os pares propriedade/valor pretendidos:
_$ (entmake (list (cons 0 "CIRCLE")
(cons 62 3)
(cons 10 (xy 4 4))
(cons 40 10)))
((0 . "CIRCLE") (62 . 3) (10 4 4 0) (40 . 10))

H duas importantes consideraes a fazer relativamente criao de


entidades:
230

O par que contm o tipo da entidade tem de ser o primeiro ou o segundo elemento da lista. Se for o segundo, o primeiro ter de ser o
nome da entidade que ser simplesmente ignorado.
Embora no o tenhamos feito no exemplo anterior, a especificao
do marcador de subclasse tornou-se obrigatria em todas as verses
recentes do AutoCad. O marcador de subclasse corresponde a duas
ocorrncias do cdigo DXF 100, uma com a string AcDbEntity e outra com a classe de entidade (AcDbCircle, AcDbText, AcDbArc,
AcDbLine, AcDbPoint, etc). Isso quer dizer que a expresso anterior deveria ter a seguinte forma:
(entmake (list (cons
(cons
(cons
(cons
(cons
(cons

0 "CIRCLE")
100 "AcDbEntity")
100 "AcDbCircle")
62 3)
10 (xy 4 4))
40 10)))

Contudo, para preservar a compatibilidade dos programas anteriores


introduo do marcador de subclasse, existem vrios tipos de entidades em relao aos quais a funo entmake ignora a presena do
marcador na lista de associaes.58 A prtica, nestes casos, nem sequer incluir as associaes com esse marcador. Seguindo esta prtica,
apenas faremos referncia a este marcador quando tal for absolutamente necessrio.
Se a operao entmake for bem sucedida, devolve a lista de associaes
usada. Se for mal sucedida, devolve nil.
Tal como acontecia com a funo entmod, a utilizao crua da funo entmake complexa, produzindo programas muito pouco legveis.
Particularmente difcil de perceber o significado dos nmeros (cdigos
DXF) que representam propriedades. Para resolver este problema, semelhana do que fizemos com os selectores e modificadores, podemos definir um construtor que aceite todos os parmetros da criao do crculo,
escondendo assim a utilizao dos cdigos DXF. Infelizmente, no caso do
construtor existem demasiados parmetros e vrios deles so opcionais.
lgico que tenhamos de especificar o centro e o raio de um crculo mas a
cor, o layer, o tipo de linha, etc, s sero necessrios em casos raros. Seria um incmodo para o utilizador do construtor ter de passar todos esses
parmetros.
58

A lista de marcadores ignorados inclui, entre outros, AcDbText, AcDb2dVertex,


AcDb3dPolylineVertex, AcDbPolygonMeshVertex, AcDbPolyFaceMeshVertex,
AcDbFaceRecord, AcDb2dPolyline, AcDb3dPolyline, AcDbArc, AcDbCircle,
AcDbLine, AcDbPoint, AcDbFace, AcDbPolyFaceMesh, AcDbPolygonMesh,
AcDbSolid.

231

9.5

Listas de Propriedades

Para resolver estes problemas vamos seguir uma abordagem diferente. Ao


invs de aceitar um nmero fixo de argumentos, o construtor de crculos
aceitar uma lista de argumentos de tamanho varivel. A ideia ser poder
criar um crculo da seguinte forma:59
(cria-circulo
(list centro: (xy 10 5)
raio: 25
cor: 5))

H dois detalhes importantes a salientar do exemplo anterior. O primeiro detalhe que, ao invs de usarmos os cdigos numricos DXF, usmos nomes lgicos facilmente compreensveis, como centro:, raio: e
cor:. O segundo detalhe que, ao invs de usarmos uma lista de associaes em que cada associao um par propriedade/valor, da forma
((p0 . v0 ) (p1 . v1 ) ... (pn . vn )), usmos uma lista simples contendo uma sucesso de propriedades e valores, da forma (p0 v0 p1 v1 ... pn vn ).
A este tipo de lista d-se o nome de lista de propriedades ou, abreviadamente,
plist.
Uma lista de propriedades plist muito semelhante a uma lista de associaes alist. A diferena fundamental est no facto de as associaes
estarem espalmadas na prpria lista. Isso faz com que as listas de propriedades sejam mais legveis do que as listas de associaes.
No entanto, trivial converter listas de propriedades em listas de associaes. Mais uma vez, o pensamento recursivo ajuda: se a lista de propriedades uma lista vazia, ento a lista de associaes correspondente
tambm uma lista vazia. Caso contrrio, formamos um par com os dois
primeiros elementos da lista de propriedades (para formar uma associao) e juntamos esse par ao resultado de converter a lista sem esses dois
elementos.
(defun alist<-plist (plist)
(if (null plist)
(list)
(cons (cons (car plist) (cadr plist))
(alist<-plist (cddr plist)))))

Do mesmo modo, trivial converter listas de associaes em listas de


propriedades. O raciocnio idntico:
59

Noutros dialectos de Lisp (mas no no Auto Lisp) possivel dispensar a invocao da


funo list pois possivel definir funes com um nmero varivel de argumentos que
constroem a lista de argumentos automaticamente.

232

(defun plist<-alist (alist)


(if (null alist)
(list)
(cons (caar alist)
(cons (cdar alist)
(plist<-alist (cdr alist))))))

A partir de uma lista de propriedades com os argumentos pretendidos


para a criao da figura geomtrica temos agora de lhe juntar o tipo pretendido: (0 . "CIRCLE") no caso de crculos, (0 . "LINE") no caso de
linhas, etc. Para evitar ter de escrever sempre o mesmo programa, vamos
primeiro generalizar um pouco definindo uma funo que recebe o tipo de
entidade (i.e., a string correspondente) e a lista de propriedades e invoca a
funo entmake com a converso dessa lista de propriedades para o formato de lista de associaes requerido:
(defun cria-entidade (tipo params)
(entmake (cons (cons 0 tipo)
(alist<-plist params))))

A funo anterior permite criar entidades arbitrrias. agora trivial


definir a funo que cria crculos:
(defun cria-circulo (params)
(cria-entidade "CIRCLE" params))

Finalmente, apenas falta definir os nomes lgicos correspondentes s


propriedades. Obviamente, o valor associado a cada nome lgico o cdigo DXF correspondente. Para um crculo, temos:
(setq centro: 10
raio: 40)

Outras propriedades so de uso mais geral:


(setq tipo-linha: 6
cor: 62
layer: 8
espessura: 39)

Para algumas propriedades faz tambm sentido dar nomes lgicos aos
possveis valores. Por exemplo, no caso da visibilidade, cujo cdigo DXF
60, os possveis valores so 0 quando visivel e 1 quando invisivel:
(setq visibilidade: 60
visivel 0
invisivel 1)

Desta forma, podemos criar figuras geomtricas de forma muito mais


legvel. Por exemplo, para criar um crculo invisvel basta-nos escrever:
233

(cria-circulo
(list centro: (xy 1 2)
raio: 5
visibilidade: invisivel))

Podemos agora definir mais umas figuras geomtricas, bem como as


propriedades que lhes esto associadas. Por exemplo, para um arco de
crculo podemos reutilizar as propriedades do crculo centro: e raio: e
acrescentar mais algumas especficas dos arcos:
(defun cria-arco (params)
(cria-entidade "ARC" params))
(setq arco-angulo-inicial: 50
arco-angulo-final: 51)

Para uma linha temos:


(defun cria-linha (params)
(cria-entidade "LINE" params))
(setq ponto-inicial: 10
ponto-final: 11)

Note-se, no programa anterior, que o nome ponto-inicial tem o


mesmo valor que o nome centro. Obviamente, ambos designam aquilo
que anteriormente denominmos de ponto primrio mas muito mais claro
distinguir os diferentes usos.
Uma outra entidade que pode vir a ser til a elipse. Para alm da
necessidade de definio de constantes para as suas propriedades especficas, a entidade elipse no dispensa a utilizao do marcador de classe
AcDbEllipse, tal como referimos na seco 9.4. Assim, temos:
(setq marcador-subclasse: 100
extremidade-eixo-maior: 11
eixo-menor/eixo-maior: 40
elipse-angulo-inicial: 41
elipse-angulo-final: 42)
(defun cria-elipse (params)
(cria-entidade
"ELLIPSE"
(append (list marcador-subclasse: "AcDbEntity"
marcador-subclasse: "AcDbEllipse")
params)))

Note-se que acrescentmos (na posio correcta) as duas ocorrncias da


propriedade 100 relativa ao marcador de subclasse.
Existem dois detalhes importantes relacionados com as propriedades
da elipse. O primeiro que a propriedade extremidade-eixo-maior:
234

pressupe um ponto relativamente ao referncial do centro da elipse. Isto


quer dizer que as coordenadas da extremidade do eixo maior da elipse so
dadas como se a origem do sistema de coordenadas estivesse colocada no
centro da elipse. O segundo que a propriedade eixo-menor/eixo-maior
representa a razo de proporo entre o comprimento do eixo menor e o do
eixo maior. Por exemplo, se o eixo menor tiver 2m de comprimento e o
maior tiver 3m, a propriedade eixo-menor/eixo-maior dever ter associado o valor 2/3 = 0.66666.
Exercicio 9.1 Defina uma funo flor capaz de desenhar uma flor custa da sobreposio de elipses rodadas, tal como se apresenta na seguinte imagem:

A funo flor dever receber, como parmetros, as coordenadas do centro da elipse,


o comprimento do semi-eixo maior e do semi-eixo menor e o nmero de ptalas a desenhar
de forma a completar uma revoluo. Por exemplo, dever ser possvel gerar as imagens
anteriores atravs da avaliao das seguintes expresses:
(flor
(flor
(flor
(flor
(flor
(flor

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

0.0
2.5
5.0
0.0
2.5
5.0

0.0)
0.0)
0.0)
2.5)
2.5)
2.5)

1
1
1
1
1
1

0.5
0.5
0.5
0.2
0.5
0.8

10)
20)
30)
6)
6)
6)

Finalmente, para vermos um exemplo de uma entidade bastante parametrizvel, consideremos a criao de textos. Uma consulta documentao DXF do AutoCad revela vrias propriedades que podemos definir da
seguinte forma:

235

(defun cria-texto (params)


(cria-entidade "TEXT" params))
(setq texto: 1
estilo: 7
altura: 40
rotacao: 50
escala-x: 41
inclinacao: 51
geracao: 71
normal 0
contrario 2
invertida 4
alinhamento-horizontal: 72
ah-esquerda 0
ah-centro 1
ah-direita 2
ah-alinhado 3
ah-meio 4
ah-justificado 5
alinhamento-vertical: 73
av-linha-base 0
av-baixo 1
av-meio 2
av-topo 3)

Os parmetros alinhamento-horizontal e alinhamento-vertical


permitem produzir variaes na forma como um dado texto se apresenta
em relao a dois pontos. A Figura 78 mostra essas possveis variaes.
Para se visualizar a utilizao desta ltima funo, a Figura 79 representa a imagem gerada pela seguinte expresso:
(foreach fraccao (enumera -0.5 0.5 0.1)
(cria-texto
(list ponto-inicial: (xy 0 (* 200 fraccao))
inclinacao: (* -2 fraccao)
texto: "Inclinado"
altura: 10)))

Uma outra imagem interessante a apresentada na Figura 80 que demonstra a capacidade do AutoCad para aplicar rotaes ao texto a inserir:
(foreach fraccao (enumera 0.0 1.0 0.1)
(cria-texto
(list ponto-inicial: (xy 0 0)
rotacao: (* fraccao 2 pi)
texto: "Rodado"
altura: 10)))

236

Abcde

Abcde

Abcde

Abcde

Abcde

Abcde

A
b

c
d

Abcde

Abcde

c
d

Abcde

Abcde

Abcde

A
b

Abcde

Ab
cd
e

Abcde

Ab
cd
e

Abcde

Abcde

Ab
cd
e

Abcde

Ab
cd
e

Abcde

Ab
cd
e

Abcde

Abcde

b
c
d
A
e
c
d
A
b
e

Abcde

c
d

Abcde

Abcde

A
b

Abcde

Abcde

Abcde

Figura 78: Possibilidades de posicionamento de texto em relao a dois


pontos. De baixo para cima o parmetro alinhamento-vertical
toma os valores de 0 a 4. Da esquerda para a direita, o parmetro
alinhamento-horizontal toma os valores de 0 a 6.

Inclinado
Inclinado
Inclinado
Inclinado
Inclinado
Inclinado
Inclinado

Inclinado

Inclinado

Inclinado

Inclinado
Figura 79: Um texto com diferentes inclinaes.

237

o
d
a
d
Ro
oda
do

o
d
a
d
Roo
Rodado
da
do

do
da
Roo
Rodado
d
a
d
o

do
oda
Ro
d
a
d
o

Figura 80: Um texto com diferentes rotaes.


Finalmente, na Figura 81 apresentamos uma combinao entre sucessivas rotaes do texto e redues no tamanho da letra. A imagem foi gerada
pela expresso:
(foreach fraccao (enumera 0.0 1.0 0.1)
(cria-texto
(list ponto-inicial: (xy 0 0)
rotacao: (* fraccao 2 pi)
texto: "Rodado"
altura: (* (- 1 fraccao) 10))))

Exercicio 9.2 Embora os exemplos anteriores mostrem que a criao de entidades razoavelmente simples, nem todos os tipos de entidades possuem esta caracterstica. A criao
de um texto multilinha (tipo MTEXT) um exemplo de uma entidade cuja especificao DXF
complexa. Para alm de obrigar a lista de associaes a incluir a presena do marcador de
subclasse para textos multilinha (AcDbMText), existe ainda a necessidade de partir o texto
em subtextos com, no mximo 250 caracteres. Isso claro a partir da especificao desta
entidade que, para os cdigos DXF 1 e 3, afirma:
1 String de texto. Se a string tiver 250 (ou menos) caracteres, todos aparecem no cdigo
1. Caso contrrio, a string dividida em pores de 250 caracteres que aparecem
associadas a um ou mais cdigos 3. Se for usado algum cdigo 3, a ltima poro
fica associada ao cdigo 1 e possui menos de 250 caracteres.60
3 Texto adicional (em pores de 250 caracteres) (opcional)
60

Na verdade, a documentao DXF do AutoCad apenas refere os casos de strings com


menos de 250 caracteres e de strings com mais de 250 caracteres, deixando na dvida o que
acontece no caso de strings com exactamente 250 caracteres. Por experimentao verifica-se
que este caso tambm fica associado ao cdigo DXF 1.

238

do

do
a
d
Ro

oda

o
d
a
d
o
Rodado
R

a
d
o

Rodado
R
R
o
d

o
R
Roodad

Ro

da

do

o
ad
od

dad
o

Figura 81: Um texto com diferentes rotaes e escalas.


Esta documentao mostra que a criao de um texto multilinha implica particionar o
texto em subfragmentos que devero ser agrupados na lista de associaes conjuntamente
com os cdigos 1 e 3. Tendo este comportamento em conta, defina a funo cria-texto-multilinha
que, a partir de uma string e de uma lista de propriedades, particiona a string e acrescenta
as associaes necessrias lista de propriedades para a correcta criao da entidade.
Exercicio 9.3 Consulte a documentao DXF do AutoCad para splines e defina uma funo cria-spline bem como as constantes que considerar relevantes para permitir a fcil
criao de curvas spline.

9.6

Leitura e Modificao de Entidades

A partir do momento em que associmos nomes s propriedades podemos


tirar partido disso para redefinir as operaes que obtm e modificam o
valor das propriedades das entidades grficas:
(defun obtem-propriedade (propriedade entidade)
(cdr (assoc propriedade (entget entidade))))
(defun muda-propriedade (propriedade novo-valor entidade)
(entmod (list (cons -1 entidade)
(cons propriedade novo-valor))))

Usando estas operaes, podemos tambm redefinir alguns dos selectores e modificadores que tnhamos introduzido anteriormente. Por exemplo,
para a cr, temos:
(defun cor (entidade)
(obtem-propriedade cor: entidade))
(defun muda-cor (entidade nova-cor)
(muda-propriedade cor: nova-cor entidade))

239

Exercicio 9.4 Defina a funo area-entidade-circulo que, dada uma entidade do tipo
circulo com um raio r, calcula a sua rea atravs da frmula A = r2 .
Exercicio 9.5 Defina a funo area-entidade-elipse que, dada uma entidade do tipo
elipse, calcula a sua rea. Dada uma elipse de semi-eixos a e b, a sua rea A dada por
A = ab.

9.7

Eliminao de Entidades

Para se eliminar uma entidade, o AutoCad disponibiliza a funo entdel


que recebe, como argumento, a entidade a eliminar. Se a entidade j tiver
sido eliminada e o desenho a que ela pertencia ainda no tiver sido encerrado, a invocao desta funo repe a entidade no desenho.
Para se determinar se uma entidade est eliminada, podemos usar a
funo entget com a entidade em questo como argumento. Se essa entidade estiver eliminada, a funo entget devolver nil. Este comportamente visvel na seguinte interaco com o Auto Lisp:
_$ (cria-circulo
;;criamos um circulo
(list centro: (xy 1 2) raio: 10))
((0 . "CIRCLE") (10 1 2) (40 . 10))
_$ (setq circulo (entlast)) ;;guardamo-lo numa variavel
<Entity name: 7efa26d8>
_$ (entget circulo)
;;obtemos as suas propriedades
((-1 . <Entity name: 7efa26d8>) (0 . "CIRCLE") ...)
_$ (entdel circulo)
;;eliminamo-lo
<Entity name: 7efa26d8>
_$ (entget circulo)
;;obtemos as suas propriedades de novo
nil
;;e nada recebemos
_$ (entdel circulo)
;;"deseliminamo-lo"
<Entity name: 7efa26d8>
_$ (entget circulo)
;;obtemos as suas propriedade de novo
((-1 . <Entity name: 7efa26d8>) (0 . "CIRCLE") ...)

9.8

ltima Entidade

No passado dissemos que a funo entlast devolve a ltima entidade criada. Podemos agora, mais correctamente, dizer que esta funo devolve a
ltima entidade no eliminada, tal como se pode ver no seguinte exemplo:

240

_$ (cria-circulo
(list centro: (xy 1 2) raio: 10))
((0 . "CIRCLE") (10 1 2) (40 . 10))
_$ (tipo-entidade (entlast))
"CIRCLE"
_$ (cria-linha
(list ponto-inicial: (xy 3 4) ponto-final: (xy 5 6)))
((0 . "LINE") (10 3 4) (11 5 6))
_$ (tipo-entidade (entlast))
"LINE"
_$ (entdel (entlast))
<Entity name: 7efcaab8>
_$ (tipo-entidade (entlast))
"CIRCLE"

9.9

Iterao de Entidades

O AutoCad disponibiliza ainda uma operao para iterar as entidades de


um desenho: entnext.
Quando invocada sem argumentos, a funo devolve a primeira entidade no eliminada (ou nil, se no existir nenhuma). Quando invocada
com uma entidade como argumento, a funo devolve a entidade (no eliminada) seguinte (ou nil se o argumento for a ltima entidade no eliminada). Tal como se v no seguinte exemplo, este comportamento permite
iterar ao longo de todas as entidades do desenho:
_$ (command "_.erase" "_all" "")
nil
_$ (entnext)
nil
_$ (cria-circulo
(list centro: (xy 1 2)
raio: 10))
((0 . "CIRCLE") (10 1 2) (40 . 10))
_$ (cria-linha
(list ponto-inicial: (xy 3 4)
ponto-final: (xy 5 6)))
((0 . "LINE") (10 3 4) (11 5 6))
_$ (entnext)
<Entity name: 7efcaac0>
_$ (entnext (entnext))
<Entity name: 7efcaac8>
_$ (entnext (entnext (entnext)))
nil

Como base nesta funo podemos construir outras bastante mais teis.
Por exemplo, dada uma entidade podemos coleccionar numa lista todas as
entidades que foram criadas depois dela:

241

(defun entidades-desde (actual)


(if (null actual)
(list)
(cons actual
(entidades-desde (entnext actual)))))

Tendo em conta que a invocao (entnext) (i.e., sem argumentos)


devolve a primeira entidade de todas, podemos agora trivialmente coleccionar todas as entidades presentes no desenho actual:
(defun todas-entidades ()
(entidades-desde (entnext)))

Continuando o exemplo anterior, temos:


_$ (todas-entidades)
(<Entity name: 7efcaac0> <Entity name: 7efcaac8>)

9.10

Entidades Compostas

At agora vimos que cada figura geomtrica que criamos no AutoCad possui uma entidade associada. No entanto, algumas das figuras geomtricas
disponibilizadas pelo AutoCad so demasiadamente complexas para poderem ser representadas por uma nica entidade. Por exemplo, uma linha
poligonal a trs dimenses61 necessita no s de uma entidade para representar as caractersticas gerais da linha como tambm necessita de uma
sucesso de entidades para representar a sucesso dos vrtices da linha.
Por ser composta por vrias subentidades, uma linha poligonal diz-se uma
entidade composta.
Aps a criao de uma linha poligonal, embora a entidade que a representa seja acedvel usando a funo entlast, para se aceder s entidades
que representam os vrtices necessrio iterar a partir da primeira, usando
a funo entnext, tal como perceptvel na seguinte interaco:62
61

Verses recentes do AutoCad permitem um outro tipo de linha poligonal denominada


linha poligonal leve que representada por uma nica entidade. Esta linha, contudo, estritamente bidimensional e no permite o mesmo grau de sofistificao das linhas poligonais
3D.
62
Para facilitar a leitura, omitimos alguma da informao produzida.

242

_$ (command "_.3DPOLY"
(xy -1 -1 0) (xy 1 0 1) (xy 0 1 -1)
"")
nil
_$ (entget (entlast))
((-1 . <Entity name: 7efcb1f0>)
(0 . "POLYLINE") ...
(10 0.0 0.0 0.0) ...)
_$ (entget (entnext (entlast)))
((-1 . <Entity name: 7efcb1f8>)
(0 . "VERTEX") ...
(10 -1.0 -1.0 0.0) ...)
_$ (entget (entnext (entnext (entlast))))
((-1 . <Entity name: 7efcb200>)
(0 . "VERTEX") ...
(10 1.0 0.0 1.0) ...)
_$ (entget (entnext (entnext (entnext (entlast)))))
((-1 . <Entity name: 7efcb208>)
(0 . "VERTEX") ...
(10 0.0 1.0 -1.0) ...)
_$ (entget (entnext (entnext (entnext (entnext (entlast))))))
((-1 . <Entity name: 7efcb210>)
(0 . "SEQEND") ...)
_$ (entnext (entnext (entnext (entnext (entnext (entlast))))))
nil

Repare-se nos tipos das entidades que so acedidas: embora a primeira


seja do tipo POLYLINE, as seguintes so do tipo VERTEX e, finalmente, a
ltima do tipo SEQEND. Esta ltima entidade usada para marcar o
fim da sequncia de entidades que constituem a entidade composta.
Com base nesta informao estamos em condies de definir funes
que processem as linhas poligonais. Por exemplo, para sabermos quais
so as coordenadas dos vrtices de uma linha poligonal, podemos simplesmente comear com a entidade que representa a linha e ir recursivamente
recolhendo as coordenadas das entidades seguintes at atingir a entidade
cujo tipo SEQEND:
(defun junta-vertices (entidade)
(if (= (tipo-entidade entidade) "SEQEND")
(list)
(cons (ponto-primario entidade)
(junta-vertices (entnext entidade)))))
(defun vertices-linha-3d (linha)
(junta-vertices (entnext linha)))

agora bastante mais simples obter as coordenadas que definem a linha


poligonal:
_$ (vertices-linha-3d (entlast))
((-1.0 -1.0 0.0) (1.0 0.0 1.0) (0.0 1.0 -1.0))

Para se criar uma linha poligonal, necessrio usar o processo inverso.


243

Dada a lista de coordenadas dos vrtices da linha a criar, criamos a entidade


que representa a linha poligonal e iteramos ao longo da lista, criando um
vrtice para cada elemento. Finalmente, terminamos com a entidade que
marca o fim da sequncia. Para tornar a especificao das propriedades
mais clara, conveniente definirmos previamente algumas constantes:
(setq tipo-linha: 70
linha-aberta 0
linha-fechada 1
linha-3d 8
malha-poligonal-3d 16
tipo-vertice: 70
vertice-extra 1
vertice-tangente 2
vertice-spline 8
vertice-controlo 16
vertice-3d 32
vertice-malha 64)

A linha poligonal pode agora ser criada usando a seguinte funo:


(defun cria-linha-3d (vertices)
(cria-entidade "POLYLINE"
(list tipo-linha: polilinha-3d))
(foreach vertice vertices
(cria-entidade "VERTEX"
(list centro: vertice
tipo-vertice: vertice-3d)))
(cria-entidade "SEQEND" (list)))

Note-se que a funo anterior apenas cria um tipo especfico de linha


poligonal.

9.11

Redesenho de Entidades

H uma funo relacionada com entidades que embora no seja geralmente


necessria, pode vir a ser til: entupd. Esta funo recebe uma entidade
como argumento e redesenha-a no ecran.
Em geral, nunca preciso usar esta funo pois o AutoCad encarrega-se
de redesenhar automaticamente as entidades que mudam. No entanto, no
caso de entidades cujo processo de modificao moroso (como os vrtices
de uma linha poligonal a trs dimenses), o AutoCad no faz o redesenho
automaticamente. Nestes casos, fica responsabilidade do programador
invocar a funo entupd sobre a entidade modificada.

9.12

Criao de Linhas Poligonais Leves

As linhas poligonais descritas na seco 9.10 so entidades complexas compostas por vrias subentidades. Nesta seco vamos ver uma variante mais
244

ligeira dessas linhas poligonais que usada pelo AutoCad sempre que
lida com linhas poligonais bidimensionais.
Contrariamente a uma linha poligonal genrica, uma linha poligonal
leve uma nica entidade. Os vrtices da linha poligonal so representados como mltiplas ocorrncias de uma mesma propriedade cujo cdigo
DXF o nmero 10. Estes vrtices so relativos a um sistema de coordenadas prprio de cada entidade, denominado sistema de coordenadas do objecto
(do Ingls Object Coordinate System ou, abreviadamente, OCS). Sempre que
se pretender conhecer as coordenadas dos vrtices da linha poligonal relativamente ao sistema de coordenadas do mundo (do Ingls World Coordinate
System ou, abreviadamente, WCS) necessrio fazer uma transformao de
coordenadas, implementada pela funo trans.
Para construirmos uma destas linhas vamos definir a seguinte funo:
(defun cria-polilinha-leve (params)
(cria-entidade "LWPOLYLINE" params))

Infelizmente, no caso de polilinhas leves, a construo mais complexa


do que anteriormente pois, por um lado, no possvel omitir o marcador
de subclasse (ver seco 9.4) e, por outro, necessrio associar um cdigo
DXF a cada vrtice. Por exemplo, para criarmos um rectngulo com os
vrtices nas coordenadas (10, 20), (20, 20), (20, 40) e (10, 40), teremos de
avaliar a seguinte expresso:
(cria-polilinha-leve
(list 100 "AcDbEntity"
100 "AcDbPolyline"
70 1
90 4
10 (xy 10 20)
10 (xy 20 20)
10 (xy 20 40)
10 (xy 10 40)))

;;marcador de subclasse
;;idem
;;tipo de linha fechada
;;numero de vertices
;;vertice
;;idem
;;idem
;;idem

Uma vez que incmodo termos de especificar sistematicamente os


marcadores de subclasse e o nmero de vrtices, vamos redefinir a funo anterior de modo a que ela aceite tambm a seguinte sintaxe para criar
o mesmo quadrado:
(cria-polilinha-leve
(list vertices: (list (xy 10 20)
(xy 20 20)
(xy 20 40)
(xy 10 40))
tipo-linha: linha-fechada))

A ideia consiste em inventar uma nova opo vertices: (com um cdigo que no interfira com os cdigos DXF pr-definidos) e, sempre que
245

ele aparece, processamos a lista que lhe est associada de modo a gerar
o cdigo DXF correcto para cada vrtice. Note-se que os restantes parmetros no devem ser afectados, permitindo misturar as vrias formas de
especificar os vrtices. Por exemplo, se tivermos os vrtices p0 , . . . , p4 e a
lista de parmetros for (10 p0 70 1 -1000 (p1 p2 p3 ) 10 p4 ), a sua converso ir produzir a lista (10 p0 70 1 10 p1 10 p2 10 p3 10 p4 ). Este
comportamento est incorporado no seguinte programa onde comeamos
por definir as constantes relevantes:
(setq vertices: -1000
numero-vertices: 90
vertice: 10)
(defun converte-parametros (params)
(cond ((null params)
(list))
((= (car params) vertices:)
(append (converte-vertices (cadr params))
(converte-parametros (cddr params))))
(t
(cons (car params)
(cons (cadr params)
(converte-parametros (cddr params)))))))
(defun converte-vertices (vertices)
(if (null vertices)
(list)
(cons vertice:
(cons (car vertices)
(converte-vertices (cdr vertices))))))

Para sabermos o nmero de vrtices precisamos agora de contabilizar


as ocorrncias do cdigo associado a vertice:.
(defun conta-parametros-vertice (params)
(cond ((null params)
0)
((= (car params) vertice:)
(1+ (conta-parametros-vertice (cddr params))))
(t
(conta-parametros-vertice (cddr params)))))

Finalmente, podemos combinar tudo numa s funo:


(defun cria-polilinha-leve (params)
(setq params (converte-parametros params))
(cria-entidade
"LWPOLYLINE"
(append (list marcador-subclasse: "AcDbEntity"
marcador-subclasse: "AcDbPolyline"
numero-vertices:
(conta-parametros-vertice params))
params)))

Para vermos um exemplo da utilizao desta funo, consideremos o


246

desenho de polgonos que foi descrito na seco 8. Na altura, definimos


uma funo que, a partir das coordenadas dos vrtices do polgono, desenhavao como uma sucesso de linhas:
(defun poligono (vertices)
(command "_.line")
(foreach vertice vertices
(command vertice))
(command "_close"))

Admitindo que todas as coordenadas so bi-dimensionais, podemos redefinir a funo de modo a usar a criao de polilinhas leves:
(defun poligono (vertices)
(cria-polilinha-leve
(list tipo-linha: linha-fechada
vertices: vertices)))

Obviamente, com esta nova definio todos os exemplos que foram


apresentados na seco 8 continuam a produzir os mesmos resultados. A
diferena estar apenas na performance e no facto de a utilizao de entidades no obedecer aos parmetros que afectam o command, nomeadamente,
o UCS, o SNAP, etc.

9.13

Manipulao de Linhas Poligonais Leves

Para alm de podermos construir linhas poligonais leves, podemos ter a


necessidade de as manipular.
Para a maioria das propriedades como, por exemplo, o tipo-linha:,
podemos continuar a usar o selector obtem-propriedade. No entanto,
para obtermos os vrtices da linha poligonal leve (no referencial OCS) temos de empregar uma funo diferente da obtem-propriedade pois esta
apenas devolve o valor associado primeira ocorrncia da propriedade na
lista de associaes enquanto que agora precisamos de construir uma lista
com todas as ocorrncias da propriedade vertice:. Assim, temos:
(defun valores-propriedade (propriedade lista-assoc)
(cond ((null lista-assoc)
(list))
((= propriedade (caar lista-assoc))
(cons (cdar lista-assoc)
(valores-propriedade propriedade
(cdr lista-assoc))))
(t
(valores-propriedade propriedade
(cdr lista-assoc)))))

Usando esta funo agora trivial definir:


247

(defun vertices-polilinha-leve (entidade)


(valores-propriedade vertice: (entget entidade)))

claro que se quisermos ter uma s funo que produz a lista de vrtices de uma linha poligonal qualquer, seja ela uma POLYLINE ou uma
LWPOLYLINE, basta-nos testar o tipo de entidade e usar a funo apropriada:
(defun vertices-polilinha (entidade / tipo)
(setq tipo (tipo-entidade entidade))
(cond ((= tipo "POLYLINE")
(vertices-polilinha-3d entidade))
((= tipo "LWPOLYLINE")
(vertices-polilinha-leve entidade))))

9.14

Seleco Interactiva de Entidades

Vimos que a funo entlast permite aceder ltima entidade criada e


que a funo entnext permite iterar todas as entidades. Infelizmente, estas duas funes no so suficientes para permitir identificar uma entidade
escolhida pelo utilizador.
Quando necessrio pedir ao utilizador que seleccione uma entidade
presente num desenho pode-se empregar a funo entsel que recebe, opcionalmente, um texto a apresentar ao utilizador. Quando invocada, a funo comea por apresentar o texto e, de seguida, aguarda que o utilizador use o rato. Quando o utilizador clica num objecto grfico qualquer, a
funo devolve uma lista contendo, como primeiro elemento, a entidade
seleccionada e, como segundo elemento, as coordenadas do ponto onde o
utilizador clicou.
_$ (entsel "Escolha uma entidade")
(<Entity name: 7efcaac0> (7.04109 -5.97352 0.0))

A razo porque devolvido tambm o ponto clicado prende-se com o


facto de podermos usar essa informao para, de alguma forma, modificarmos a entidade. Por exemplo, se pretendermos partir uma linha em duas
num dado ponto, podemos definir a seguinte funo:
(defun parte-linha (linha ponto)
(cria-linha
(list
ponto-inicial: (obtem-propriedade ponto-inicial: linha)
ponto-final: ponto))
(cria-linha
(list
ponto-inicial: ponto
ponto-final: (obtem-propriedade ponto-final: linha)))
(entdel linha))

Podemos agora usar a funo entsel para obter a linha que pretendemos partir em duas e o ponto nessa linha onde a queremos partir, passando
248

esses valores separadamente para a funo parte-linha. isso que a


funo seguinte faz:
(defun comando-parte-linha (/ seleccao)
(setq seleccao (entsel))
(parte-linha (car seleccao) (cadr seleccao)))

9.15

Definio de Comandos AutoCad

At agora, todas as operaes que definimos apenas podiam ser invocadadas a partir do Auto Lisp. Para quem gosta de alisp, isso suficiente,
mas para o utilizador menos experiente de AutoCad isso pode rapidamente
tornar-se inconveniente: na maioria dos casos ser prefervel poder invocar
funes sem abandonar o ambiente do AutoCad e sem sequer ter se saber
a sintaxe do Lisp.
Para isso, o Auto Lisp permite a definio de comandos AutoCad. Essa
definio feita de forma idntica definio de funes, sendo a distino feita apenas por uma conveno de nomes: para se definir o comando
nome, basta definir uma funo cujo nome ser c:nome e cujo corpo ir
conter as aces que se pretende que o comando realize. Uma vez que a
funo se destina a ser invocada como se de um comando AutoCad se tratasse, ela no pode receber argumentos e, consequentemente, no pode ter
parmetros. No entanto, embora se destine fundamentalmente a ser usada
no AutoCad, o comando definido no deixa de estar implementado por
uma funo Lisp e, por isso, pode tambm ser invocado a partir do Lisp.
semelhana de qualquer outra invocao de funo, o comando nome assim definido pode ser invocado como (c:nome). Note-se que o nome da
funo, em Auto Lisp, aquele que foi empregue na definio. apenas
para o AutoCad que o nome perde o prefixo c:.
Na definio de comandos, importante termos em conta que se o comando a definir tiver o mesmo nome que um comando pr-definido, ele s
ficar activo quando indefinirmos o comando pr-definido.
Usando a definio de comandos AutoCad podemos tornar mais prtica a operao de partir uma linha:
(defun c:parte-linha (/ seleccao)
(setq seleccao (entsel))
(parte-linha (car seleccao) (cadr seleccao)))

Note-se que a funo anterior no recursiva: a funo c:parte-linha


invoca a funo parte-linha mas trata-se de duas funes diferentes.
No entanto, com a definio anterior fica disponvel no AutoCad um novo
comando denominado parte-linha que, quando invocado, provoca a
invocao da funo c:parte-linha que, por sua vez, invoca a funo
parte-linha.

249

Funo
getint
getreal
getstring
getpoint
getcorner
getdist
getangle

getorient

Tipo de Resultado
Um inteiro.
Um real.
Uma string.
Um ponto obtido na rea de desenho ou na linha de comandos.
Um ponto relativo a um outro ponto dado, obtido na rea
de desenho ou na linha de comandos.
Uma distncia obtida na rea de desenho ou na linha de
comandos.
Um ngulo (em radianos) obtido na rea de desenho ou
na linha de comandos. O ngulo zero dado pela varivel
ANGBASE e cresce segundo ANGDIR.
Um ngulo (em radianos) obtido na rea de desenho ou
na linha de comandos. O ngulo zero sempre para a
direita e cresce segundo ANGDIR.

Tabela 6: Funes de obteno de informao pr-definidas do Auto Lisp.

9.16

Obteno de Informao

Vimos que a funo entsel permite pedir ao utilizador para seleccionar


uma entidade, entidade essa que ser devolvida juntamente com as coordenadas do ponto clicado. Em muitos casos, contudo, podemos necessitar
tambm de outras informaes. Por exemplo, podemos pedir ao utilizador que indique pontos na sua rea de desenho. Outra possibilidade ser
pedirmos nmeros ou textos ou nomes de ficheiros.
Para cada um destes fins, o Auto Lisp disponibiliza uma funo prdefinida: getpoint, getcorner, getint, getreal, getstring, getfiled,
etc. Este conjunto de funes est descrito na Tabela 6. Qualquer delas recebe um argumento opcional que uma string a apresentar ao utilizador
para o informar do que se pretende.
ainda possvel restringir os valores que se podem obter a partir das
funes anteriores atravs da utilizao da funo initget, por exemplo,
proibindo os valores negativos ou limitando os pontos obtidos a terem uma
coordenada z nula.

9.17

reas de Polgonos

Vimos que a definio de funes Auto Lisp pode ser usada tambm para
definir comandos AutoCad. Nesta seco vamos combinar a definio de
comandos com a utilizao de funes que pedem informaes ao utilizador.
A ttulo de exemplo, consideremos um comando para criar uma diviso
250

rectangular em plantas de habitaes. O comando ir pedir a posio dos


cantos da diviso e, com esses valores, ir construir o rectngulo correspondente e colocar no interior desse rectngulo uma linha de texto com a rea
da diviso.
Para implementarmos este comando vamos comear por definir a funo divisao-rectangular que, a partir do canto inferior esquerdo e do
canto superior direito, constri o rectngulo em questo e insere o texto
pretendido:
(defun divisao-rectangular (p0 p1 / meia-altura)
(cria-polilinha-leve
(list tipo-linha: linha-fechada
vertices: (list p0
(xy (cx p1) (cy p0))
p1
(xy (cx p0) (cy p1)))))
(setq meia-altura (/ (+ (cy p0) (cy p1)) 2.0))
(cria-texto
(list ponto-inicial: (xy (cx p0) meia-altura)
ponto-final: (xy (cx p1) meia-altura)
altura: 1
texto: (strcat " Area:"
(rtos (* (- (cx p1) (cx p0))
(- (cy p1) (cy p0))))
" ")
alinhamento-horizontal: ah-alinhado
alinhamento-vertical: av-meio)))

Uma vez que tem parmetros, esta funo apenas pode ser invocada
a partir do Auto Lisp. Isso suficiente para testarmos o seu funcionamento mas no suficiente para o seu uso a partir do AutoCad. O objectivo, agora, criar um comando que recolhe do utilizador os dados
suficientes para conseguir invocar a funo anterior. Para evitar estarmos sempre a definir novos nomes, vamos reutilizar o nome da funo
divisao-rectangular para baptizar o comando:
(defun c:divisao-rectangular (/ p0 p1)
(setq p0 (getpoint "\nEspecifique o primeiro canto")
p1 (getcorner "\nEspecifique o segundo canto" p0))
(divisao-rectangular p0 p1))

Infelizmente, a definio anterior tem um problema: apenas funciona


correctamente se o segundo canto ficar acima e direita do primeiro canto.
Esse problema visvel na Figura 82 onde acrescentmos tambm uma seta
desde o primeiro ponto fornecido at o segundo ponto fornecido.
Para corrigirmos o problema, necessitamos de processar os pontos recolhidos de forma a invocarmos a funo divisao-rectangular com o
canto inferior esquerdo e com o canto superior direito do rectngulo virtual recolhido. precisamente isso que faz a seguinte redefinio:
251

Area:40.000

Area:-40.000

Area:40.000

Area:-40.000

Figura 82: Divises rectangulares com a rea ou a orientao do texto incorrectamente calculadas.

Area:19.349
Area:5.162

Area:7.737
Area:6.360
Area:15.854

Area:28.597

Figura 83: Divises rectangulares criadas com o comando AutoCad


divisao-rectangular.
(defun c:divisao-rectangular (/ p0 p1)
(setq p0 (getpoint "\nEspecifique o primeiro canto")
p1 (getcorner "\nEspecifique o segundo canto" p0))
(divisao-rectangular (xy (min (cx p0) (cx p1))
(min (cy p0) (cy p1)))
(xy (max (cx p0) (cx p1))
(max (cy p0) (cy p1)))))

Com esta correco agora possvel criar, interactivamente, a planta de


uma habitao e anotar cada diviso criada com a informao da sua rea,
tal como ilustramos na Figura 83.
Embora o comando divisao-rectangular permita criar divises onde
automaticamente calculada e escrita a sua rea, existem duas importantes
limitaes:
Apenas permite criar reas rectangulares alinhadas com os eixos x e
y.
252

No permite inserir a rea em figuras geomtricas j existentes num


desenho.
Estas limitaes sugerem que repensemos o comando. A primeira limitao resolve-se de forma trivial: no vale a pena ser o comando a criar o
rectngulo pois o AutoCad j possui uma operao para isso, denominada
rectangle. Na realidade, o AutoCad j disponibiliza muitas operaes
capazes de criar as mais variadas figuras geomtricas, pelo que intil estarmos a tentar replicar essa funcionalidade. A segunda limitao um
pouco mais difcil de resolver pois o clculo da rea e do ponto de insero
dessa rea depende da figura geomtrica em questo, o que nos vai obrigar
a considerar vrias formas diferentes de o fazermos.63
Comecemos por analisar o clculo da rea de uma entidade geomtrica.
Se a entidade for um crculo de raio r, a rea r2 mas se for uma elipse de
semi-eixos a e b, a rea ab. Isto permite-nos definir as funes:
(defun area-entidade-circulo (entidade)
(* pi (quadrado (obtem-propriedade raio: entidade))))
(defun area-entidade-elipse (entidade / eixo-maior)
(setq eixo-maior
(pol-ro (obtem-propriedade extremidade-eixo-maior: entidade)))
(* pi
eixo-maior
(obtem-propriedade eixo-menor/eixo-maior: entidade)
eixo-maior))

Para podermos tratar de forma genrica qualquer das duas entidades


anteriores, podemos definir uma funo que distingue os dois casos:
(defun area-entidade (entidade / tipo)
(setq tipo (tipo-entidade entidade))
(cond ((= tipo "CIRCLE")
(area-entidade-circulo entidade))
((= tipo "ELLIPSE")
(area-entidade-elipse entidade))))

Quanto ao ponto de insero, vamos considerar que o texto ir ficar


centrado na entidade, pelo que podemos definir uma operao genrica
que distingue os vrios tipos de entidade:64
63

Verses recentes do AutoCad so capazes de determinar a rea e o centro de um vasto


conjunto de entidades. No entanto, para efeitos pedaggicos, vamos admitir que essa funcionalidade no existe.
64
No caso dos crculos e elipses, seria possvel combinar as duas situaes numa s pois
a propriedade para aceder ao centro da entidade a mesma. No entanto, para tornar o
programa mais claro, vamos manter a separao.

253

(defun centro-entidade (entidade / tipo)


(setq tipo (tipo-entidade entidade))
(cond ((= tipo "CIRCLE")
(obtem-propriedade centro: entidade))
((= tipo "ELLIPSE")
(obtem-propriedade centro: entidade))))

A partir do momento em que temos funes genricas que nos do a


rea e o centro de uma entidade, podemos definir a funo (igualmente
genrica) que, a partir de uma entidade, insere no seu centro um texto a
descrever a sua rea:
(defun insere-area-entidade (entidade / centro)
(setq centro (centro-entidade entidade))
(cria-texto
(list ponto-inicial: centro
ponto-final: centro
altura: 1.5
texto: (strcat "Area:"
(rtos (area-entidade entidade)))
alinhamento-horizontal: ah-centro
alinhamento-vertical: av-meio)))

Por fim, podemos definir um comando no AutoCad que, aps pedir ao


utilizador que seleccione uma entidade, invoca a funo anterior usando a
entidade seleccionada como argumento:
(defun c:insere-area ()
(insere-area-entidade
(car (entsel "Seleccione a entidade pretendida"))))

Como bvio, o comando anterior apenas pode ser aplicado a crculos


e elipses, o que limita excessivamente a sua utilidade. Vamos, portanto,
modific-lo para lidar com outras entidades, por exemplo, rectngulos,
pentgonos, etc.
Uma caracterstica interessante do AutoCad que nos simplifica muito o
problema o facto de a maioria das figuras geomtricas poligonais estarem
implementadas usando o mesmo tipo de linhas poligonais que discutimos
na seco 9.12. De facto, o AutoCad no possui um tipo de entidade especfico para rectngulos ou pentgonos, optando por implement-los como
sequncias de linhas poligonais leves, tal como verificvel nas seguintes
interaces:

254

(x1 , y1 )

(x0 , y0 )

x
Figura 84: Decomposio de um polgono arbitrrio em trapzios rectangulares.
_$ (command "_.rectangle" (xy 1 2) (xy 3 4))
nil
_$ (tipo-entidade (entlast))
"LWPOLYLINE"
_$ (command "_.polygon" 5 (xy 1 2) "_inscribed" 3)
nil
_$ (tipo-entidade (entlast))
"LWPOLYLINE"

Assim, podemos preocupar-nos apenas em lidar com este tipo de entidades e, automaticamente, ficaremos habilitados a lidar com uma enorme
variedade de figuras geomtricas. Temos, portanto, de encontrar uma forma
de calcular a rea e o centro de um qualquer polgono delimitado por linhas
poligonais leves.
Para calcular a rea de um polgono podemos empregar uma abordagem simples que foi inventada independentemente pelos matemticos
Meister e Gauss em finais do sculo dezoito. A ideia consiste em decompor
o polgono num conjunto de trapzios rectangulares, tal como se apresenta
na Figura 84. Se for [(x0 , y0 ), (x1 , y1 ), (x2 , y2 ), . . . , (xn1 , yn1 ), (xn , yn )] a
sequncia de coordenadas dos vrtices do polgono, o primeiro trapzio
ter os vrtices em (x0 , 0), (x0 , y0 ), (x1 , y1 ) e (x1 , 0), o segundo em (x1 , 0),
(x1 , y1 ), (x2 , y2 ) e (x2 , 0), o penltimo em (xn1 , 0), (xn1 , y0 ), (xn , yn ) e
(xn , 0) e o ltimo trapzio ter os vrtices em (xn , 0), (xn , yn ), (x0 , y0 ) e
(x0 , 0).
A rea de cada um destes trapzios pode ser calculada pela frmula
1
A = (xi+1 xi )(yi+1 + yi)
2
sendo que a diferena xi+1 xi ser positiva ou negativa consoante xi menor ou maior que xi+1 , implicando que a rea do trapzio respectivo ser
255

tambm positiva ou negativa. Ora sendo o polgono fechado, se percorrermos os vrtices por ordem, por exemplo, segundo os ponteiros do relgio,
necessriamente que alguns dos vrtices tero coordenadas xi sucessivamente crescente enquanto outros a tero sucessivamente decrescente. No
difcil vermos que ao somarmos os trapzios com rea positiva aos trapzios com rea negativa, iremos obter a rea total do polgono. Essa rea
poder ser positiva ou negativa consoante a ordem escolhida para percorrer os vrtices do polgono mas, em qualquer caso, ser a rea total do
polgono.
Traduzindo este raciocnio para Lisp, podemos definir as seguintes funes que, a partir de uma lista com as coordenadas dos vrtices do poligono, calculam a sua rea:
(defun area-vertices (vertices)
(if (null (cdr vertices))
0
(+ (area-trapezio (car vertices) (cadr vertices))
(area-vertices (cdr vertices)))))
(defun area-trapezio (p0 p1)
(* 0.5
(- (cx p1) (cx p0))
(+ (cy p0) (cy p1))))
Exercicio 9.6 Embora correcto, o algoritmo anterior realiza trabalho desnecessrio. Isso
torna-se bvio quando desenvolvemos o clculo para um polgono qualquer. Por exemplo, num polgono de trs vrtices, temos:
A=

1
1
1
(x1 x0 )(y1 + y0 ) + (x2 x1 )(y2 + y1 ) + (x0 x2 )(y0 + y2 )
2
2
2

ou seja
1
A = (x1 y1 + x1 y0 x0 y1 x0 y0 )+
2
1
(x2 y2 + x2 y1 x1 y2 x1 y1 )+
2
1
(x0 y0 + x0 y2 x2 y0 x2 y2 )
2
evidente, na frmula anterior, que h clculos que so feitos repetidamente mas com
sinais opostos. Eliminando os termos que se anulam, obtemos:
1
A = (x1 y0 x0 y1 )+
2
1
(x2 y1 x1 y2 )+
2
1
(x0 y2 x2 y0 )
2
No difcil generalizar a frmula anterior para uma sequncia arbitrria de vrtices,
permitindo-nos obter uma simplificao do processo de clculo:
A=

n1
1X
xi+1 yi xi yi+1
2 i=0

Redefina a funo area-vertices de forma a tirar partido desta simplificao.

256

H um detalhe do algoritmo anterior que ainda no est tratado: o ltimo trapzio tem vrtices em (xn , 0), (xn , yn ), (x0 , y0 ) e (x0 , 0), mas a lista
de vrtices termina em (xn , yn ). Uma forma trivial de implementarmos
este detalhe consiste em acrescentarmos o vrtice (x0 , y0 ) ao fim da lista de
vrtices. precismente isso que a seguinte funo faz onde aproveitamos
tambm para obter o valor absoluto para termos sempre uma rea positiva:
(defun area-poligono (vertices)
(abs (area-vertices (append vertices (list (car vertices))))))

Finalmente, podemos aplicar este clculo a qualquer polgono fechado


criado custa de linhas poligonais leves:
(defun area-polilinha-leve (entidade)
(area-poligono (vertices-polilinha-leve entidade)))

Uma vez calculada a rea de um polgono arbitrrio, apenas nos falta


definir o centro (
x, y) do polgono para podermos inserir o texto que des65
creve essa rea. O centro de um polgono pode-se definir por um processo
idntico ao do clculo da rea, somando, de forma ponderada, os centros
dos vrios trapzios:
P
x
i Ai
x
= Pi
i Ai
P
yi Ai
y = Pi
i Ai
Uma maneira simples de calcularmos simultaneamente x
e y consiste
em trat-los como coordenadas (
x, y) e empregarmos as operaes de coordenadas xy e +xy.
Para comear, precisamos de definir o centro de um trapzio de lados a
e b e base d. A frmula matemtica deste centro
x
=

y =

d(2a + b)
3(a + b)

a2 + ab + b2
3(a + b)

As frmulas anteriores so vlidas para trapzios com o canto inferior na


origem e com lados a ou b no nulos. Para lidarmos com as situaes em
que tal no acontece, vamos definir uma funo Lisp mais sofisticada:
65

Quando falamos de polgonos arbitrrios usual designar-se o seu centro pelo termo
centride.

257

(defun centro-trapezio (p0 p1 / a b d)


(setq a (cy p0)
b (cy p1)
d (- (cx p1) (cx p0)))
(if (= a b 0)
(xy (+ (cx p0) (/ d 2))
0)
(xy (+ (cx p0)
(/ (* d (+ (* 2 a) b))
(* 3 (+ a b))))
(/ (+ (* a a) (* a b) (* b b))
(* 3 (+ a b))))))

Para calcular cada um dos produtos x


i Ai e yi Ai que ocorrem como termos do numerador das fraces anteriores vamos definir a funo centro-area-trapezio:
(defun centro-area-trapezio (p0 p1 / centro area)
(setq centro (centro-trapezio p0 p1)
area (area-trapezio p0 p1))
(xy (* (cx centro) area)
(* (cy centro) area)))

A funo anterior trata de um s trapzio. Para fazer o somatrio de


todos os trapzios, definimos:
(defun centro-area-vertices (vertices)
(if (null (cdr vertices))
(xy 0 0)
(+c (centro-area-trapezio (car vertices) (cadr vertices))
(centro-area-vertices (cdr vertices)))))

Finalmente, para determinar o centro do polgono, temos de dividir o


resultado calculado pela funo anterior pela rea total do polgono:
(defun centro-poligono (vertices / centro-area area)
(setq vertices (append vertices (list (car vertices))))
(setq centro-area (centro-area-vertices vertices)
area (area-vertices vertices))
(xy (/ (cx centro-area) area)
(/ (cy centro-area) area)))

Uma vez que estamos particularmente interessados em tratar polgonos


que, sabemos, o AutoCad implementa com linhas poligonais leves, vamos
criar uma nova funo que, a partir de uma entidade construda com uma
linha poligonal leve, calcula o seu centro a partir dos seus vrtices:
(defun centro-polilinha-leve (entidade)
(centro-poligono (vertices-polilinha-leve entidade)))

Finalmente, falta apenas generalizar as funes area-entidade e centro-entidade


para tratarem tambm as linhas poligonais leves:
258

Area:5.890

Area:9.511

Area:12.100

Area:7.069

Area:15.901

Figura 85: Insero de reas de entidades.


(defun area-entidade (entidade / tipo)
(setq tipo (tipo-entidade entidade))
(cond ((= tipo "CIRCLE")
(area-entidade-circulo entidade))
((= tipo "ELLIPSE")
(area-entidade-elipse entidade))
((= tipo "LWPOLYLINE")
(area-polilinha-leve entidade))))
(defun centro-entidade (entidade / tipo)
(setq tipo (tipo-entidade entidade))
(cond ((= tipo "CIRCLE")
(obtem-propriedade centro: entidade))
((= tipo "ELLIPSE")
(obtem-propriedade centro: entidade))
((= tipo "LWPOLYLINE")
(centro-polilinha-leve entidade))))

agora possvel seleccionar entidades de tipos variados e inserir automaticamente a sua rea, tal como se apresenta na Figura 85
Exercicio 9.7 Um semi-octaedro uma pirmide de base quadrangular que se define por
cinco pontos: quatro para a base e um para o topo ou znite.
Considere a criao de um comando AutoCad denominado semi-octaedro que pede
ao utilizador as cinco coordenadas necessrias para definir um semi-octaedro e cria as linhas correspondentes s arestas do slido.

259

9.18

Entidades com Informao Adicional

Vimos que o AutoCad nos permite obter um conjunto vasto de informao


sobre as entidades que foram criadas, incluindo os pontos primrios e secundrios, as dimenses, o tipo de linha, a cor, etc. Vamos agora ver que o
AutoCad tambm permite acrescentar informaes adicionais a essas entidades, informaes essas que so guardadas no prprio documento em que
estamos a trabalhar, permitindo a sua recuperao numa sesso posterior.
Esta possibilidade particularmente interessante para progamas Auto
Lisp que pretendam associar algum significado s entidades geomtricas
que esto a ser criadas. A ttulo de exemplo, considermos a planta de uma
habitao on se empregaram diferentes entidades geomtricas para fazer
a diviso do espao em diferentes reas. Do ponto de vista do AutoCad,
essas entidades so meras figuras geomtricas sem significado mas, para
o arquitecto que idealiza a planta, cada uma representa um uso especfico,
como seja, um quarto, uma sala, uma cozinha. Para que esta informao
no se perca, necessria a associao deste uso a cada uma das entidades
permitindo, por exemplo, que um programa Auto Lisp possa verificar se
uma dada planta cumpre as normas legais relativas s dimenses mnimas
que cada uso implica.
Antes de vermos como que esta associao de informao pode ser
feita importante termos em conta que o AutoCad agnstico quanto ao
que cada programa Auto Lisp possa querer associar a cada entidade, permitindo que diferentes programas o possam fazer em simultneo. Este
agnosticismo d aos programas a liberdade de incluirem as informaes
que pretenderem mas tem o potencial de permitir que um programa possa
inadvertidamente usar e manipular informao que foi adicionada por outro programa diferente. Para evitar este problema, o AutoCad exige que a
informao adicionada a uma entidade seja particionada de acordo com o
programa que a est a adicionar. Para isso, temos de registar o programa,
i.e., temos de lhe dar um nome que o distinga de outros programas e temos
de informar o AutoCad acerca desse nome. Isso feito atravs da funo
regapp.66 Note-se que o nome uma string que no pode ter mais de 31
caracteres e cuja sintaxe semelhante de um smbolo Lisp.
Para exemplificarmos, consideremos um programa Auto Lisp destinado
a associar um determinado uso a cada entidade geomtrica representada
numa planta. Comecemos por baptizar este programa:
(regapp "USO")

A partir deste momento, podemos usar o nome "USO" para associar informaes a qualquer entidade. Para isso, o AutoCad reserva um conjunto
66

Cada programa Auto Lisp considerado pelo AutoCad como uma aplicao. Esta terminologia reflecte-se no nome da funo regapp pois ele constitui uma contraco de register
application.

260

Cdigo
1000
1001
1002
1010
1040
1070
1071

Significado
Uma string at 255 caracteres.
O nome de uma aplicao.
O nome de um layer.
Uma coordenada tridimensional.
Um valor real.
Um valor inteiro de 16 bits.
Um valor inteiro de 32 bits.

Tabela 7: Cdigos DXF para informaes adicionais. Cada cdigo representa um tipo particular de dados.
de cdigos DXF que podem ser usados repetidamente para categorizar a
informao associada. A Tabela 7 explica alguns desses cdigos. Da observao da tabela conclumos, por exemplo, que para associarmos uma string
a uma entidade temos de empregar o cdigo DXF 1000. Vrios outros cdigos existem, incluindo cdigos que permitem estruturar a informao em
vrios nveis de detalhe.
Uma vez que estes cdigos e os valores associados tm de ser particionados por aplicao, a lista de associaes tem de ser agrupada numa outra
lista que contm o cdigo DXF 3, indicador de que h informaes adicionais associadas entidade. Por exemplo, para atribuirmos o uso sala
ltima entidade criada temos de avaliar a seguinte expresso:
(entmod
(list (cons -1 (entlast))
(cons -3 (list
(cons "USO"
(list (cons 1000 "sala")))))))

Para recuperar aquela informao, temos de ter em conta que, por omisso, a funo entget no devolve a informao adicional e, para a obtermos, temos de especificar num parmetro adicional uma lista contendo as
aplicaes cuja informao tambm pretendemos.
_$ (assoc -3 (entget (entlast) (list "USO")))
(-3 ("USO" (1000 . "sala")))

Obviamente, aquelas formas de associar e obter informaes so muito


pouco claras. Felizmente, fcil escondermos a complexidade do processo
dentro de duas funes que denominaremos adiciona-informacao e
obtem-informacao.
Para facilitar ainda mais o uso da funo adiciona-informacao, vamos adoptar uma abordagem idntica que descrevemos na Seco 9.5, em
que empregmos uma lista de propriedades para conter os parmetros da
261

funo, lista essa que depois convertemos para uma lista de associaes tal
como a funo entmod pretende:
(defun adiciona-informacao (entidade aplicacao plist)
(entmod
(list (cons -1 entidade)
(cons -3 (list
(cons aplicacao (alist<-plist plist)))))))

Assim, a atribuio do uso sala ltima entidade criada passa a ser


feita da seguinte forma:
(adiciona-informacao (entlast) "USO" (list 1000 "sala"))

Para recuperarmos a informao que adicionmos a uma entidade vamos definir a funo obtem-informacao. Esta funo precisa de saber
qual a aplicao e qual a propriedade especifica dessa aplicao que foram
usadas para guardar a informao. Tendo em conta a estrutura de listas de
associao encadeadas que usada para guardar informao adicional em
entidades, a definio da funo fica:
(defun obtem-informacao (entidade aplicacao propriedade)
(cdr (assoc propriedade
(cdadr (assoc -3
(entget entidade (list aplicacao)))))))

Assim, para obter o uso da ltima entidade criada, temos de escrever:


_$ (obtem-informacao (entlast) "USO" 1000)
"sala"

Como o uso explcito de cdigos DXF no ajuda compreenso do que


se est a fazer, podemos tornar os programas mais claros se escondermos
esses cdigos em variveis com nomes apropriados. A seguinte atribuio
define alguns desses cdigos:
(setq tipo-string 1000
tipo-xyz 1010
tipo-real 1040
tipo-inteiro 1071)

claro que podemos simplificar ainda mais os nossos programas se


criarmos funes ainda mais especficas, desenhadas apenas para atribuir
e recuperar usos:
(defun atribui-uso (entidade uso)
(adiciona-informacao entidade "USO" (list tipo-string uso)))

262

(defun obtem-uso (entidade)


(obtem-informacao entidade "USO" tipo-string))

Para permitir a sua invocao a partir do AutoCad, podemos tambm


definir um comando equivalente:
(defun c:atribui-uso ()
(atribui-uso
(car (entsel "Seleccione a entidade"))
(getstring "Qual o uso da entidade seleccionada?")))

Atravs deste comando, o utilizador pode definir (e redefinir) o uso de


qualquer entidade. Naturalmente, a definio do uso de pouco serve se no
for possvel recuperar essa informao. Para exemplificarmos a utilidade
desta informao, podemos definir um comando AutoCad que itere por
todas as entidades e escreva uma listagem dos seus usos e reas:
(defun c:usos-e-areas (/ posicao-texto uso)
(setq posicao-texto (getpoint "Posicione o texto..."))
(foreach entidade (todas-entidades)
(setq uso (obtem-uso entidade))
(if uso
(progn
(cria-texto
(list ponto-inicial: posicao-texto
texto: (strcat uso ":" (rtos (area-entidade entidade)))
altura: 1))
(setq posicao-texto (+y posicao-texto -1.5))))))

A Figura 86 mostra um exemplo da utilizao desta funo numa planta


com vrios tipos de reas.

9.19

Escadas

Uma escada uma sucesso de degraus que estabelece uma ligao entre
dois nveis. Os degraus so agrupados em lanos e os lanos so interligados por patamares. Cada degrau caracterizado pelo comprimento c do seu
cobertor e pela altura e do seu espelho, tal como se representa na Figura 88.
Notemos, nessa figura, que uma escada com n cobertores tem n + 1 espelhos. Assim, dado um nmero de cobertores n, o comprimento l dada
por l = nc mas a altura h vencida pelos correspondentes n + 1 espelhos
h = (n + 1)e.
Imaginemos agora que pretendemos definir um comando que nos permita criar escadas facilmente. Para isso, recomendvel dividirmos o problema em dois subproblemas:
1. Primeiro, definimos o programa Auto Lisp que, a partir dos parmetros geomtricos do problema, produz a sua soluo. Este programa
263

Area:5.890

Area:9.511

Quarto:9.511
Sala:15.901
Cozinha:12.100
Tanque:7.069
Piscina:5.890

Area:12.100

Area:7.069

Area:15.901

Figura 86: Uma planta com uma legenda produzida automaticamente a


partir dos usos e reas das entidades geomtricas.

Figura 87: Uma escada na ilha de Santorini, na Grcia. Fotografia de Christopher Riess.

264

P1

h
e

P0

Figura 88: Parametrizao de uma escada.


deve ser desenvolvido segundo uma perspectiva matemtica e sem
preocupaes com o modo como o programa vai ser usado pelo utilizador.
2. Segundo, observamos o programa desenvolvido no ponto anterior
e, a partir da determinao dos parmetros necessrios para o usar,
definimos um comando AutoCad que pede esses parmetros ao utilizador (segundo uma ordem que faa sentido) e, de seguida, invoca
o programa anterior, passando-lhe os valores fornecidos pelo utilizador.
Seguindo esta metodologia, vamos comear por definir uma funo
que, a partir de um ponto P , do comprimento e altura de cada degrau
e do nmero de degraus, desenha um lano de escada. Como habitualmente, esta funo pode ser decomposta recursivamente no desenho de
um degrau seguido do desenho da restante escada. No caso de paragem
desenhamos apenas o espelho do ltimo degrau pois o cobertor correspondente j pertence ao patamar ou andar onde a escada termina.
Uma vez que o final de cada elemento da escada ponto de partida
para o elemento seguinte, ser til saber qual foi o ponto onde terminou o
elemento anterior (seja degrau ou apenas espelho). Por este motivo, vamos
definir as funes degrau e espelho de modo a devolverem o ponto final
onde terminaram o desenho.
(defun degrau (p c e / pm pf)
(setq pm (+xy p 0 e)
pf (+xy p c e))
(command "_.line" p pm pf "")
pf)

265

(defun espelho (p c e / pf)


(setq pf (+xy p 0 e))
(command "_.line" p pf "")
pf)

Estas duas funes sero agora usadas para definir o caso recursivo e o
caso final da funo lanco-escada: um lano de escada com n degraus
obtido atravs da criao de um degrau seguido da criao de um lano
de escada com n 1 degraus a comear do ponto onde terminou o degrau
anterior; um lano de escada com 0 degraus meramente um espelho.
(defun lanco-escada (p c e n)
(if (= n 0)
(espelho p c e)
(lanco-escada (degrau p c e) c e (- n 1))))

A funo lanco-escada resolve o problema geomtrico mas no


prtica para quem est no AutoCad e precisa de criar uma escada entre
dois pontos. Para tornarmos a sua utilizao mais simples, vamos criar
um comando AutoCad que, a partir de dois pontos P0 e P1 e do nmero
n de cobertores pretendidos, calcula a dimenso de cada degrau e invoca
aquela funo com os parmetros adequados. Para isso, temos de calcular
o tamanho do cobertor e do espelho a partir das distncias horizontal l e
vertical h entre aqueles pontos, o que podemos fazer a partir das frmulas
(
c = nl
h
e = n+1
Assim, temos:
(defun c:lanco-escada (/ p0 p1 l h n)
(setq p0 (getpoint "\nPonto inicial da escada?")
p1 (getpoint "\nPonto final da escada?")
l (- (cx p1) (cx p0))
h (- (cy p1) (cy p0))
n (getint "\nNmero de cobertores?"))
(lanco-escada
p0
(/ l n)
(/ h (+ n 1))
n))

Podemos agora invocar no AutoCad o comando lanco-escada, o que


levar o AutoCad a pedir-nos que indiquemos com o cursor grfico o ponto
inicial e o ponto final da escada e, finalmente, o nmero de cobertores desejado. Em relao a este ltimo parmetro, o AutoCad ir responsabilizar-se
por verificar e garantir que o utilizador fornece, de facto, um nmero inteiro.67 A Figura 89 apresenta um conjunto de escadas criadas a partir de
mltiplas invocaes do comando AutoCad lanco-escada.
67

Para este problema, seria til que se conseguisse garantir, no s que a resposta do uti-

266

Figura 89: Escadas criadas por mltiplas invocaes do comando AutoCad


lanco-escada.
Obviamente, podemos usar a funo lanco-escada para construir escadas mais complexas, compostas por vrios lanos de escadas separados
por patamares, tal como se apresenta na Figura 90. Para isso, mais uma
vez, vamos comear por resolver o problema geomtrico.
Em primeiro lugar, vamos definir a funo que desenha um patamar a
partir de um ponto (que dever corresponder ao final de um espelho) e que
devolve o ponto final do patamar:
(defun patamar (p lpatamar / pf)
(setq pf (+xy p lpatamar 0))
(command "_.line" p pf "")
pf)

Em segundo lugar, vamos definir a funo lancos-escada que, a partir de um ponto de partida p, de um comprimento de cobertor c, de uma
altura de espelho e, de um nmero de cobertores n por lano, de um nmero total de lanos e do comprimento de cada patamar, desenha a escada
separando-a em vrios lanos. A funo ter de distinguir o caso em que
s tem de desenhar um lano de escadas daquele em que tem de desenhar
mais do que um. Neste ltimo caso, a funo ter que desenhar tambm
um patamar:
(defun lancos-escada (p c e n lancos lpatamar)
(if (= lancos 1)
(lanco-escada p c e n)
(lancos-escada
(patamar (lanco-escada p c e n) lpatamar)
c e n (- lancos 1) lpatamar)))

A Figura 91 ilustra o uso desta funo. As escadas a representadas


foram geradas pelas seguintes expresses:
lizador corresponde a um nmero inteiro, mas tambm que esse nmero inteiro positivo.
A funo getint apenas garante a primeira parte.

267

Figura 90: Uma escada composta por dois lanos com um patamar intermdio. Fotografia de Mateusz Szczerbinski.
(lancos-escada (xy 0.0 0.0) 0.26 0.16 10 4 1.0)
(lancos-escada (xy 10.0 0.0) 0.26 0.36 10 2 2.0)
(lancos-escada (xy 13.0 0.0) 0.16 0.12 8 7 0.5)

Tal como aconteceu com a funo lanco-escada, podemos tornar a


funo lancos-escada mais acessivel ao utilizador de AutoCad atravs
da definio de um comando que pede os parmetros geomtricos necessrios, calcula os outros que deles dependem e, finalmente, invoca a funo:

Figura 91: Lanos de escada criados por mltiplas invocaes da funo


Auto Lisp lancos-escada.

268

(defun c:lancos-escada (/ p0 p1 h l lancos lpatamar n ntotal)


(setq p0 (getpoint "\nPonto inicial da escada?")
p1 (getpoint "\nPonto final da escada?")
h (- (cy p1) (cy p0))
l (- (cx p1) (cx p0))
lancos (getint "\nNmero de lanos?")
lpatamar (getreal "\nComprimento do patamar?")
n (getint "\nNmero de cobertores por lano?")
ntotal (* lancos n))
(lancos-escada
p0
(/ (- l (* (- lancos 1) lpatamar)) ntotal)
(/ h (* lancos (+ n 1)))
n
lancos
lpatamar))
Exercicio 9.8 A funo c:lancos-escada apresenta um comportamento bizarro quando
o ponto inicial P0 se encontra direita do ponto final P1 . Neste caso, tal como se pode
ver na figura abaixo, a escada desenhada fica com um patamar que se desloca na direco
contrria.
P1

P0
Modifique a funo c:lancos-escada de modo a que o patamar fique orientado na
mesma direco da escada, a menos que o utilizador fornea um comprimento de patamar
negativo, caso em que o patamar dever ficar com a direco oposta.

9.19.1

Dimensionamento de Escadas

Como fcil de observar nas Figuras 89 e 91, nem todas as escadas so


suficientemente ergonmicas para poderem ser usadas por seres humanos
pois necessrio, por exemplo, que o espelho de um degrau no seja to
grande que impea a pessoa de o conseguir subir ou to pequeno que se
torne desconfortvel. Tendo em conta as dimenses humanas, razovel
convencionar que o espelho e tem de estar compreendido entre um valor
mnimo emin e um valor mximo emax (que, para um adulto, se convenciona
que emin = 0.14m e emax = 0.18m). Isto implica que o conjunto de equaes

269

que regulam as dimenses de uma escada passa a ser

c = n
h
e = n+1

emin e emax
Para alm disto ainda necessrio ter em conta que o nmero de cobertores n tem de ser um nmero inteiro.
Estas restries tornam mais difcil o dimensionamento de uma escada
pois impedem-nos de usar um nmero arbitrrio de cobertores. Felizmente, podemos usar o Auto Lisp e desenvolver um algoritmo que calcula
quais so os nmeros aceitveis apenas a partir do comprimento l e altura
h da escada. Para isso, substitumos a segunda equao na terceira, ficando
emin

h
emax
n+1

ou seja
h
h
1n
1
emax
emin
Para incluirmos a restrio de que n tem de ser um nmero inteiro,
basta-nos acrescentar os arredondamentos adequados:
l h
m
j h
k
1 n
1
emax
emin
Assim, dada uma altura h e as alturas mnimas emin e mximas emax do
espelho, podemos obter o nmero mnimo e mximo de cobertores. Por
exemplo, consideremos uma escada que necessita de vencer uma altura
h = 3.55m usando degraus com um espelho e entre emin = 0.14m e emax =
0.18m. Substituindo os valores na inequao anterior, temos d18.7e n
b24.4c, i.e., poderemos usar 19, 20, 21, 22, 23 ou 24 cobertores. Estamos
ento em condies de implementar uma funo que nos devolve um par
com este nmero mnimo e mximo de cobertores:
(defun cobertores (h emin emax / nmin nmax)
(setq nmin (ceiling (- (/ h emax) 1))
nmax (fix (- (/ h emin) 1)))
(cons nmin nmax))

Podemos agora confirmar o nmero mnimo e mximo de cobertores:


_$ (cobertores 3.55 0.14 0.18)
(19 . 24)

Infelizmente, a funo cobertores no tem em conta todas as restries ergonmicas: para alm das limitaes na altura do espelho e de um
270

degrau ainda necessrio termos em conta que a dimenso do cobertor c


no pode ser to pequena que no permita apoiar um p, i.e., temos de ter
c cmin (em termos humanos, usual considerar cmin = 0.23m). Como
l
c = nl , temos que nl cmin , ou seja, n cmin
. Mais uma vez, tendo em
l
conta que n tem de ser um nmero inteiro, ficamos com n b cmin
c.
Para exemplificar, voltemos a considerar a escada de altura h = 3.55m,
para a qual tnhamos encontrado um nmero de cobertores n entre 19 e
24 e acrescentemos agora que a escada tem tambm de cobrir um com5.10
primento l = 5.10m. Substituindo os valores, temos n b 0.23
c ou seja,
n b22.1c. Isto implica que, do conjunto de possibilidades anterior, apenas podemos escolher entre usar 19, 20, 21 ou 22 cobertores. No entanto,
se o comprimento fosse um metro mais pequeno, i.e., l = 4.10m, teriamos
n b17.8c que, infelizmente, no seria possvel compatibilizar com o intervalo de variao anterior. Nesse caso, deveramos alertar o utilizador para
o problema e, para sermos simpticos, poderamos tambm dizer-lhe qual
o comprimento mnimo da escada que satisfaz a restrico. A seguinte
redefinio da funo cobertores tem estes aspectos em conta.
(defun cobertores (h emin emax l cmin / nmin nmax)
(setq nmin (ceiling (- (/ h emax) 1))
nmax (fix (- (/ h emin) 1))
ncmax (fix (/ l cmin)))
(if (< ncmax nmin)
(alerta
(strcat "No possvel dimensionar o cobertor."
"\nAumente o comprimento para "
(rtos (* nmin cmin))))
(progn
(setq nmax (min nmax ncmax))
(cons nmin nmax))))

Para alertar o utilizador vamos simplesmente escrever a string argumento.


(defun alerta (str)
(princ str)
(princ "\n")
nil)

Podemos agora confirmar os resultados que obtivemos para os exemplos anteriores, o primeiro para um comprimento de 5.1m, o segundo para
um comprimento de 4.1m:
_$ (cobertores 3.55 0.14 0.18 5.1 0.23)
(19 . 22)
_$ (cobertores 3.55 0.14 0.18 4.1 0.23)
No possvel dimensionar o cobertor.
Aumente o comprimento para 4.3700
nil

Note-se que, para l = 4.1m, a funo cobertores no consegue cal271

cular o nmero de cobertores mas sugere uma medida para a qual j o


conseguiria fazer. Podemos ento experimentar essa medida:
_$ (cobertores 3.55 0.14 0.18 4.37 0.23)
(19 . 19)

A ltima expresso permite-nos dimensionar uma escada de 20 degraus


(19 cobertores), cada um com um cobertor de c = 4.37
19 = 0.23m e espelho
3.55
de e = 19+1
= 0.1775m.
Apesar de bastante mais ergonmicas, as nossas escadas no so ainda
totalmente ergonmicas pois sabido que o uso confortvel de uma escada
depende de uma relao de proporo entre o cobertor e o espelho de cada
degrau. Esta proporo conhecida desde o sculo dezassete e resultou
das investigaes conduzidas pelo arquitecto Franois Blondel68
. . . chaque fois quon slve dun pouce, la valeur de la partie
horizontale se trouve rduite de deux pouces et que la somme
de la hauteur double de la marche et de son giron doit demeurer constante et tre de deux pieds. . .
Os deux pieds a que Blondel faz referncia designam o mdulo b do
degrau. Uma vez que o mdulo b depende do tamanho da pessoa que
usa a escada, no existe uma medida exacta para o seu valor e, por isso,
considera-se apenas que ele deve estar contido entre dois extremos bmin
e bmax . Por exemplo, para uma pessoa adulta, usual convencionar que
bmin = 0.62m e bmax = 0.64m. Assim sendo, as observaes de Blondel
escrever-se-iam modernamente na forma:
bmin c + 2e bmax
Se aplicarmos a regra de Blondel ao degrau que calculmos atrs (que
tinha c = 0.23m e e = 0.1775m), obtemos c + 2e = 0.585 que, infelizmente,
est longe do intervalo ideal [0.62, 0.64].
Para complementarmos o nosso algoritmo com a regra de Blondel vamos comear por traduzi-la em termos do nmero de cobertores, usando
h
as relaes c = nl e e = n+1
:
bmin

l
h
+2
bmax
n
n+1

Multiplicando ambos os lados por n(n + 1), obtemos


bmin (n2 + n) l(n + 1) + 2hn bmax (n2 + n)
68

Nicolas-Franois Blondel foi um famoso arquitecto, engenheiro e matemtico francs


do sculo dezassete. Para alm de ter projectado obras arquitecturais importantes, publicou o Cours darchitecture enseign lAcadmie royale darchitecture onde descreveu as suas
descobertas sobre o dimensionamento de escadas.

272

f (n)

f (n) = Bbmax (n)

f (n) = Bbmin (n)


n

Figura 92: Curvas da funo de Blondel para b = bmin e b = bmax .


ou seja
bmin n2 (l + 2h bmin )n l 0 bmax n2 (l + 2h bmax )n l
Esta desigualdade caracteriza o nmero de cobertores n em termos dos
limites bmin e bmax do mdulo de Blondel. Uma vez que as expresses apenas diferem no mdulo b que usam (que ser bmin para uma e bmax para a
outra), podemos definir a funo de Blondel
Bb (n) = bn2 (l + 2h b)n l
e reescrever a desigualdade como
Bbmin (n) 0 Bbmax (n)
Sendo a funo de Blondel quadrtica em n e sendo que o coeficiente
de segundo grau da equao (b) sempre positivo, a curva da funo uma
parbola com a concavidade para cima, tal como se apresenta na Figura 92.
O nmero de cobertores que satisfaz a equao de Blondel ficar ento
contido no intervalo em que a curva Bbmin (n) assume valores negativos e
a curva Bbmax (n) assume valores positivos. Para determinarmos os limites
desse intervalo temos de identificar as razes das funes, i.e., os valores
de n para os quais as duas funes se tornam zero e as respectivas curvas
cruzam o eixo das abcissas. Assim, temos de resolver a equao
Bb (n) = bn2 (l + 2h b)n l = 0
Uma vez que se trata de uma equao do segundo grau, podemos aplicar a frmula resolvente para a resolver em ordem a n. Assim, temos
p
l + 2h b (l + 2h b)2 + 4bl
n=
2b
273

Das duas raizes, h uma que obviamentepimpossvel pois, sendo b > 0


e l > 0, l + 2h b estritamente menor que (l + 2h b)2 + 4bl e, consequentemente, a sua subtraco corresponderia a um nmero de cobertores
negativo, tal como visvel no grfico da funo. Assim, temos que
p
l + 2h b + (l + 2h b)2 + 4bl
n=
2b
Dado que b o coeficiente de segundo grau da equao e b sempre
positivo, a desigualdade Bbmin (n) 0 Bbmax (n) equivalente a

n l+2hbmax + (l+2hbmax )2 +4bmax l


2bmax
n l+2hbmin + (l+2hbmin )2 +4bmin l
2bmin

Para calcular os extremos do intervalo anterior, vamos comear por definir a funo que calcula o nmero de cobertores n a partir da frmula de
Blondel para o mdulo b:
(defun cobertores-blondel (h l b / l+2h-b)
(setq l+2h-b (+ l (* 2 h) (- b )))
(/ (+ l+2h-b (sqrt (+ (quadrado l+2h-b) (* 4 b l))))
(* 2 b)))

Para exemplificar, voltemos a considerar a escada de altura h = 3.55m


e comprimento l = 5.10m para a qual tnhamos encontrado um nmero de
cobertores n entre 19 e 22. Relativamente ao mdulo mximo e mnimo,
vamos usar a conveno usual bmin = 0.62m e bmax = 0.64m. Usando a
funo anterior, temos:
_$ (cobertores-blondel 3.55 5.10 0.64)
18.4934
_$ (cobertores-blondel 3.55 5.10 0.62)
19.1079

Substituindo, temos 18.5 n 19.1 ou seja, dos valores de n que


tnhamos, apenas o primeiro satisfaz todos os requisitos. Assim, para este
exemplo, temos n = 19 e, consequentemente, ficamos com degraus cujo
cobertor c e espelho e sero
(
c = 5.10
19 = 0.2684
3.55
e = 19+1 = 0.1775
Infelizmente, na prtica, a partir de um comprimento l e altura h arbitrrios, dificilmente conseguiremos encontrar valores de n que satisfaam
todas as restries. Por exemplo, se retirarmos 5cm altura anterior, i.e., se

274

a nossa escada passar a ter de vencer h = 3.50m, os intervalos de variao


do nmero de cobertores n passam a ser

18.4 n 23.5
n 22.2

18.37 n 18.95
Uma vez que n tem de ser um nmero inteiro, a terceira desigualdade
no satisfazvel. Isto implica que, tal como anteriormente, o nosso algoritmo poder no conseguir dimensionar a escada e, nesse caso, dever
avisar o utilizador de qual dever ser o comprimento adequado. Para isso,
recuperamos a frmula de Blondel
bmin

l
h
+2
bmax
n
n+1

e resolvmo-la em relao a l:
n(bmin 2

h
h
) l n(bmax 2
)
n+1
n+1

Assim, podemos definir:


(defun comprimento-blondel (h b n)
(* n (- b (/ (* 2 h) (+ n 1)))))

Podemos agora redefinir a funo cobertores para ter em conta a


regra de Blondel, no s garantindo que a regra permite encontrar um nmero inteiro de cobertores mas ainda que esse nmero est contido no intervalo de variao determinado pelas restries nas dimenses do espelho
e do cobertor. Para isso, temos de acrescentar as variveis locais nbmin e
nbmax, que representam os limites do nmero de cobertores que satisfazem a regra de Blondel, bem como as variveis nfinalmin e lfinalmax
que iro conter os limites finais aps satisfao de todas as restries.

275

(defun cobertores (h emin emax l cmin bmin bmax /


nmin nmax ncmax nbmin nbmax
nfinalmax nfinalmin)
(setq nmin (ceiling (- (/ h emax) 1))
nmax (fix (- (/ h emin) 1))
ncmax (fix (/ l cmin)))
(if (< ncmax nmin)
(alerta
(strcat "No possvel dimensionar o cobertor."
"\nAumente o comprimento para "
(rtos (* nmin cmin))))
(progn
(setq nmax (min nmax ncmax)
nbmin (cobertores-blondel h l bmax)
nbmax (cobertores-blondel h l bmin))
(if (> (ceiling nbmin) (fix nbmax))
(alerta
(strcat "No possvel respeitar a regra de Blondel."
"\nMude o comprimento para ser maior que "
(rtos (comprimento-blondel h bmin (ceiling nbmax)))
" ou menor que "
(rtos (comprimento-blondel h bmax (fix nbmin)))))
(progn
(setq nfinalmin (max nmin (ceiling nbmin))
nfinalmax (min nmax (fix nbmax)))
(if (> nfinalmin nfinalmax)
(alerta
(strcat "No possvel respeitar a regra de Blondel."
"\nMude o comprimento para ser maior que "
(rtos (comprimento-blondel h bmin nmin))
" e menor que "
(rtos (comprimento-blondel h bmax nmax))))
(cons nfinalmin nfinalmax)))))))

A seguinte interaco mostra o processo iterativo de dimensionamento


de uma escada com altura h = 2.8m e comprimento inicial l = 3.0m (sujeita
s restries habituais emin = 0.14m, emax = 0.18m, cmin = 0.23m, bmin =
0.62m e bmax = 0.64):

276

_$ (cobertores 2.8 0.14 0.18 3.0 0.23 0.62 0.64)


No possvel dimensionar o cobertor.
Aumente o comprimento para 3.4500
nil
_$ (cobertores 2.8 0.14 0.18 3.45 0.23 0.62 0.64)
No possvel respeitar a regra de Blondel.
Mude o comprimento para ser maior que 3.4533 ou menor que 3.1200
nil
_$ (cobertores 2.8 0.14 0.18 3.46 0.23 0.62 0.64)
No possvel respeitar a regra de Blondel.
Mude o comprimento para ser maior que 4.0500 e menor que 4.3500
nil
_$ (cobertores 2.8 0.14 0.18 4.06 0.23 0.62 0.64)
(15 . 15)

Reparemos que foi necessrio passar sucessivamente o comprimento da


escada do valor inicial de 3.00m para 3.45m, 3.46m e, finalmente, 4.06m.
Naturalmente, um pouco irritante ter de fazer estas iteraes manualmente. Para o evitarmos, podemos convencionar que, no caso de se violar
alguma restriao ergonmica, o algoritmo invoca-se recursivamente com
o comprimento maior da escada que evita essa violao. No final, o algoritmo devolver o comprimento final da escada e o nmero mdio de
cobertores para esse comprimento. H apenas mais um detalhe: temos de
ter cuidado com os potenciais erros de arredondamento que podem fazer
com que o comprimento que o algoritmo sugere acabe por ser insuficiente
e, por isso, obrigar o algoritmo a sugeri-lo de novo, entrando em recurso
infinita. Para contornar este problema, podemos simplesmente adicionar
um milmetro (ou, se quisermos mais preciso, uma dcima ou centsima
de milmetro) ao comprimento sugerido.
A seguinte funo comprimento-cobertores implementa este algoritmo:

277

(defun comprimento-cobertores (h emin emax l cmin bmin bmax /


nmin nmax ncmax nbmin nbmax
nfinalmax nfinalmin)
(setq nmin (ceiling (- (/ h emax) 1))
nmax (fix (- (/ h emin) 1))
ncmax (fix (/ l cmin)))
(if (< ncmax nmin)
(comprimento-cobertores
h emin emax
(+ 0.001 (* nmin cmin))
cmin bmin bmax)
(progn
(setq nmax (min nmax ncmax)
nbmin (cobertores-blondel h l bmax)
nbmax (cobertores-blondel h l bmin))
(if (> (ceiling nbmin) (fix nbmax))
(comprimento-cobertores
h emin emax
(+ 0.001 (comprimento-blondel h bmin (ceiling nbmax)))
cmin bmin bmax)
(progn
(setq nfinalmin (max nmin (ceiling nbmin))
nfinalmax (min nmax (fix nbmax)))
(if (> nfinalmin nfinalmax)
(comprimento-cobertores
h emin emax
(+ 0.001 (comprimento-blondel h bmin nmin))
cmin bmin bmax)
(cons l (round (/ (+ nfinalmin nfinalmax) 2)))))))))

Usando esta funo agora possvel calcular automaticamente a melhor escada (em termos dos critrios ergonmicos que adoptmos). Por
exemplo, a escada anterior pode ser dimensionada partindo de um comprimento 0.0:
_$ (comprimento-cobertores 2.8 0.14 0.18 0.0 0.23 0.62 0.64)
(4.051 . 15)
Exercicio 9.9 A funo cobertores comea por calcular o nmero mnimo e mximo de
cobertores a partir da altura h a vencer e das dimenses mxima e mnima do espelho (emax
e emin ):
l h
m
j h
k
1 n
1
emax
emin
As dimenses mxima e mnima do espelho dependem, naturalmente, da legislao
aplicvel. No caso portugus, por exemplo, o Regulamento Geral de Edificaes Urbanas
refere:
Os degraus da escada no podero ter largura inferior a 23 centmetros, no
contando o focinho; a sua altura deve ficar compreendida entre 14 e 18 centmetros;
Infelizmente, ao tentarmos dimensionar uma escada com uma altura de 55 centmetros
de altura, encontramos a seguinte impossibilidade:
l 0.55
m
j 0.55
k
1 n
1
0.18
0.14

278

ou seja
3n2
Redefina a funo cobertores de modo a detectar estas impossibilidades e a sugerir
alturas que as evitem.

9.19.2

Dimensionamento de Lanos

Embora o nosso algoritmo para determinao do nmero de cobertores


de uma escada seja razoavelmente sofisticado, convm recordar que ele
apenas implementa algumas das regras arquitectnicas em uso e que h
ainda mais regras que deveramos ter em conta. Por exemplo, em geral,
considera-se que no aceitvel ter um lano de escadas a vencer uma altura excessivamente grande69 , sendo obrigatrio partir a escada em vrios lanos separados por patamares com uma dimenso adequada (normalmente, correspondente a um passo, i.e., dimenso de Blondel, adicionado de um cobertor).
Para incorporarmos mais esta regra, temos de considerar a altura da
escada e vermos quantos lances ela pode conter. Para isso, basta-nos dividir a altura da escada pela altura mxima dos lances e arrendondar o
resultado para cima. A altura actual de cada lance ser ento determinada pela diviso da altura total da escada pelo nmero de lances. Dispondo da altura de um lance, podemos dimension-lo usando a funo
comprimento-cobertores e, com base no comprimento e nmero de
cobertores calculado para cada lance (bem como na altura do lance), podemos calcular a dimenso do cobertor e do espelho. Finalmente, podemos
usar a funo lancos-escada para desenhar a escada pretendida. Todos
estes clculos podem ser realizados pela seguinte funo:
(defun dimensiona-escada (p0 p1 hmax emin emax cmin bmin bmax lpatamar /
h nlancos hlanco dims llanco clanco c e)
(setq h (- (cy p1) (cy p0))
nlancos (ceiling (/ h hmax))
hlanco (/ h nlancos)
dims (comprimento-cobertores hlanco emin emax 0.0 cmin bmin bmax)
llanco (car dims)
clanco (cdr dims)
c (/ llanco clanco)
e (/ hlanco (+ clanco 1)))
(lancos-escada
(+xy p1 (- 0.0 (* nlancos llanco) (* (- nlancos 1) lpatamar)) (- h))
c e clanco nlancos lpatamar))

Para facilitar a utilizao da funo, vamos definir o seguinte comando


que pede toda a informao necessria:
69

Equivalentemente, alguns regulamentos limitam o tamanho dos lanos de escadas em


termos do seu nmero de degraus.

279

Figura 93: Lanos de escada criados por mltiplas invocaes da funo


Auto Lisp dimensiona-escada. As escadas vencem todas a mesma altura mas o nmero de lanos diminui da esquerda para a direita.
(defun c:dimensiona-escada (/ p0 p1 hmax emin emax cmin bmin bmax lpatamar)
(setq p0 (getpoint "\nPonto inicial da escada? (movel na horizontal)")
p1 (getpoint "\nPonto final da escada? (fixo)")
hmax (getreal "\nAltura mxima por lano?")
emin (getreal "\nAltura mnima do degrau?")
emax (getreal "\nAltura mxima do degrau?")
cmin (getreal "\nComprimento mnimo do degrau?")
bmin (getreal "\nDimenso mnima de Blondel?")
bmax (getreal "\nDimenso mxima de Blondel?")
lpatamar (getreal "\nComprimento do patamar?"))
(dimensiona-escada p0 p1 hmax emin emax cmin bmin bmax lpatamar))

A Figura 93 mostra uma sucesso de escadas que vencem a mesma altura mas com alturas mximas de lance sucessivamente crescentes.
Exercicio 9.10 A funo c:dimensiona-escada tem um inconveniente grave: sempre
que invocada, pede todos os parmetros necessrios ao utilizador, embora muitos desses parmetros (como, por exemplo, emin , emax , cmin , bmin e bmax ) sejam tendencialmente
constantes.
Redefina a funo c:dimensiona-escada de modo a que os parmetros possam ter
valores de omisso que o utilizador s especifica se assim o pretender.

9.19.3

El Castillo

Embora actualmente as escadas sejam objecto de um conjunto relativamente grande de regras, nem sempre isso aconteceu e existem inmeros
exemplos no passado de escadas desenhadas para fins muito mais importantes do que meramente o de permitir deslocar pessoas entre diferentes
nveis.
Um bom exemplo ocorre no templo de Cuculcn, em Chichn Itz no
Mxico, apresentado na Figura 94. Cuculcn uma divindade da mitologia
Maia, representada por uma serpente emplumada. O templo a ela dedicado ter sido construdo h mais de mil e cem anos e foi denominado El
Castillo pelos conquistadores espanhis quando encontraram as runas de
Chichn Itz. As escadas que ladeiam cada um dos quatro lados do templo
tm, cada uma, 91 degraus e vrios historiadores atribuem a este nmero
um significado cronolgico: as quatro escadas totalizam 364 degraus ao
280

Figura 94: El Castillo, o templo de Cuculcn localizado em Chichn Itz,


no Mxico. Fotografia de Filippo Manaresi.
qual se soma um degrau final no topo do templo, correspondendo aos 365
dias do ano. Outros especialistas argumentam que as escadas teriam sido
desenhadas para provocarem interessantes efeitos sonoros atravs dos diferentes ecos que os seus degraus provocariam. Finalmente, atendendo a
que estas escadas vencem um comprimento e altura de 24 metros, h quem
argumente que esta elevadssima inclinao se destinava a produzir um
efeito ptico de elevao aos cus.
Em qualquer caso, interessante constatar que as escadas do templo
de Cuculcn no seriam aprovadas pelas modernas regras de dimensionamento. Podemos facilmente confirmar isto atravs da seguinte interaco:
_$ (cobertores 24.0 0.14 0.18 24.0 0.23 0.62 0.64)
No possvel dimensionar o cobertor.
Aumente o comprimento para 30.5900
nil
_$ (cobertores 24.0 0.14 0.18 31.0 0.23 0.62 0.64)
No possvel respeitar a regra de Blondel.
Mude o comprimento para ser maior que 34.8182 e menor que 38.1156
nil
_$ (cobertores 24.0 0.14 0.18 35.0 0.23 0.62 0.64)
(133 . 133)

Como podemos ver, para que as escadas do Castillo obedeam aos


modernos critrios ergonmicos, ser necessrio passar o nmero de degraus de 91 para 134 e passar o comprimento de 24m para 35m. Ainda
assim, as escadas continuariam a violar a regra que probe lanos com uma
altura excessiva, pelo que seria necessrio introduzir patamares intermdios. Se admitirmos que cada lano no pode exceder os 3.5m de altura,
ento sero necessrios 6 patamares, fazendo o comprimento final da escada atingir cerca de 43 metros.
281

Figura 95: Esquema de El Castillo. Da esquerda para a direita, encontrase (1) a escada original do templo, (2) a escada que possui um cobertor
mnimo de 0.23m e espelho entre 0.14m e 0.18m, (3) a escada que, para
alm disso, obedece regra de Blondel (com limites entre 0.62m e 0.64m)
e, finalmente, (4) a escada que, para alm das restries anteriores, possui
lanos que no excedem os 3.5m de altura.
Para compararmos a evoluo destas escadas, a Figura 95 mostra um
esquema do templo de Cuculcn onde, da esquerda para a direita, mostramos as escadas originais do templo, as escadas que respeitam o comprimento mnimo do cobertor, as escadas que respeitam tambm a regra de
Blondel e, finalmente, as escadas finais que respeitam ainda a altura mxima dos lanos.

9.20

Caixas de Escada

Como vimos nas seces anteriores, a funo c:dimensiona-escada aumenta o comprimento da escada tanto quanto for necessrio para respeitar
as restries ergonmicas. Isto implica que se a altura a vencer for muito
elevada, como acontece com El Castillo, a escada resultante pode ficar
com um comprimento enorme.
Infelizmente, nem sempre possvel aumentar o comprimento de uma
escada. Na maioria das situaes, na realidade, quer o espao horizontal
quer o espao vertical disponveis para o desenvolvimento de uma escada
esto limitados (ou desejvel que estejam) e a soluo para vencer alturas elevadas assenta no uso de patamares intermdios e lanos de escadas
alternados, tal como se apresenta na Figura 96.
Para implementar este tipo de escadas, vamos comear por model-las
de acordo com a Figura 97. Nessa figura podemos ver vrios lanos de
escadas, interligados entre si por patamares representados por rectngulos
282

Figura 96: Escada com patamares intermdios. Fotografia de Stijn Nieuwendijk.

283

P
Figura 97: Um modelo de escadas com lanos alternados. Da esquerda
para a direita, as escadas possuem trs, quatro e cinco lanos. O ponto P
representa o nicio da primeira escada.
a tracejado.
Do ponto de vista da modelao destas escadas, os patamares so totalmente desnecessrios pois, admitindo que os patamares tm todos a
mesma dimenso, para uma dada dimenso horizontal de escada que inclua patamares trivial derivar a dimenso que no inclui os patamares.
Por este motivo, vamos considerar que o ponto de partida de cada escada
no inclui o patamar e coincide com o arranque da escada, tal como se v
representado pelo ponto P na escada mais esquerda.
Para desenharmos este tipo de escadas no temos mais do que definir
uma funo idntica funo lancos-escada mas que, de cada vez que
inicia um novo lano de escada, usa o simtrico do cobertor para efectuar a
mudana de direco:
(defun lancos-alternados-escada (p c e n lancos)
(if (= lancos 1)
(lanco-escada p c e n)
(lancos-alternados-escada
(lanco-escada p c e n)
(- c) e n (- lancos 1))))

Para dimensionarmos estas escadas temos, antes de mais, de saber quantos lanos sero necessrios para vencer uma dada altura. Para isso, importante percebermos que o nmero de lanos depende (entre outros factores) de o incio da escada ficar, ou no, do mesmo lado que o seu fim. Como
fcil percebermos pela anlise da Figura 97, se o incio da escada est do
mesmo lado que o fim, a escada ter necessariamente de ter um nmero
par de lanos. Se o incio da escada est no lado oposto ao do fim, ento
284

a escada ter necessariamente um nmero mpar de lanos. Este facto implica que, para um dado incio e fim de escada, medida que a altura da
escada aumenta, o nmero de lanos apropriado para a vencer ter sempre
de aumentar dois lanos de cada vez.
Deste modo, podemos idealizar um algoritmo para o dimensionamento
destas escadas que, para alm das medidas do espao onde temos de inserir a escada, ir tambm receber um nmero inicial de lanos pretendidos.
Se esse nmero for par, o incio e o fim da escada esto no mesmo lado,
caso contrrio, esto em lados opostos. Este algoritmo ir determinar as
dimenses do cobertor e espelho de cada lano individual e, caso eles violem as restries ergonmicas que j discutimos anteriormente, aumenta o
nmero de lanos em duas unidades e tenta de novo. Quando o nmero
de lanos se tornar to grande que o tamanho do espelho e correspondente
fica inferior altura mnima admissvel emin , o algoritmo desiste, devolvendo nil para assinalar que no possvel satisfazer os constrangimentos. Nessa altura, o utilizador dever relaxar algumas das restries como,
por exemplo, os limites da regra de Blondel.
Atendendo a este algoritmo, a seguinte funo uma adaptao da funo comprimento-cobertores mas que, no caso de violao de restries, se limita a aumentar o nmero de lanos:
(defun altura-cobertores-lancos
(lancos ht emin emax l cmin bmin bmax /
h nmin nmax ncmax nbmin nbmax nfinalmax nfinalmin)
(setq h (/ ht lancos)
nmin (ceiling (- (/ h emax) 1))
nmax (fix (- (/ h emin) 1))
ncmax (fix (/ l cmin)))
(if (< h emin)
nil
(if (< ncmax nmin)
(altura-cobertores (+ lancos 2)
ht emin emax l cmin bmin bmax)
(progn
(setq nmax (min nmax ncmax)
nbmin (cobertores-blondel h l bmax)
nbmax (cobertores-blondel h l bmin))
(if (> (ceiling nbmin) (fix nbmax))
(altura-cobertores (+ lancos 2)
ht emin emax l cmin bmin bmax)
(progn
(setq nfinalmin (max nmin (ceiling nbmin))
nfinalmax (min nmax (fix nbmax)))
(if (> nfinalmin nfinalmax)
(altura-cobertores (+ lancos 2)
ht emin emax l cmin bmin bmax)
(list h (round (/ (+ nfinalmin nfinalmax) 2)) lancos))))))))

Para facilitar a utilizao desta funo podemos definir outra que, dados dois pontos que delimitam um rectngulo, dado o nmero inicial de
285

lanos e dados os limites ergonmicos dos degraus, dimensiona a escada


que cabe naquele rectngulo e desenha-a a comear no primeiro ponto.
(defun dimensiona-escada-limitada
(p0 p1 nlancos emin emax cmin bmin bmax /
l h hlanco dims llanco clanco c e)
(setq l (- (cx p1) (cx p0))
h (- (cy p1) (cy p0))
dims (altura-cobertores-lancos
nlancos h emin emax l cmin bmin bmax))
(if (null dims)
(alerta "No possvel dimensionar a escada")
(progn
(setq hlanco (car dims)
clanco (cadr dims)
nlancos (caddr dims)
c (/ l clanco)
e (/ hlanco (+ clanco 1)))
(lancos-alternados-escada
p0 c e clanco nlancos))))

Finalmente, para podermos invocar esta funo a partir do AutoCad


vamos definir um comando que, entre outros dados, pede ao utilizador
que delimite um rectngulo onde dever ser inserida a escada e que invoca
a funo anterior.
(defun c:dimensiona-escada-limitada (/ p0 p1 nlancos emin emax cmin bmin bmax)
(setq p0 (getpoint "\nPonto inicial da escada?")
p1 (getcorner p0 "\nPonto final da escada?")
nlancos (getint "Nmero mnimo de lanos?")
emin (getreal "\nAltura mnima do degrau?")
emax (getreal "\nAltura mxima do degrau?")
cmin (getreal "\nComprimento mnimo do degrau?")
bmin (getreal "\nDimenso mnima de Blondel?")
bmax (getreal "\nDimenso mxima de Blondel?"))
(dimensiona-escada-limitada p0 p1 nlancos emin emax cmin bmin bmax))

Para ilustrar a utilizao deste comando, apresentamos na Figura 98


uma imagem onde dimensionmos vrias escadas de modo a ficarem contidas dentro dos rectngulos apresentados e onde, aps a execuo do comando, acrescentmos os patamares.

286

Figura 98: Lanos de escada criados de modo a ocuparem as reas delimitadas pelos rectngulos. Da esquerda para a direita, as escadas so dimensionadas para um nmero mnimo de dois, trs, trs e dois lanos, respectivamente, mas as suas dimenses finais implicam quatro, cinco, trs e
doze lanos, respectivamente. As escadas possuem um cobertor mnimo
de 0.23m, espelho entre 0.14m e 0.18m e limites de Blondel entre 0.62m e
0.64m.

287

Vous aimerez peut-être aussi