Você está na página 1de 178

Introduo Programao com AutoLisp

Antnio Menezes Leito Fevereiro 2007

Contedo
1 Programao 1.1 Linguagens de Programao . . . . . . . . . . . . . . . . . . . 1.2 Lisp . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.3 Exerccios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sintaxe, Semntica e Pragmtica 2.1 Sintaxe e Semntica do Lisp . . . . . . . . . . . . . . . . . . . 2.2 O Avaliador . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elementos da Linguagem 3.1 Elementos primitivos . . . . . . . . . 3.1.1 Nmeros . . . . . . . . . . . . 3.1.2 Cadeias de Caracteres . . . . 3.2 Combinaes . . . . . . . . . . . . . 3.3 Indentao . . . . . . . . . . . . . . . 3.4 Avaliao de Combinaes . . . . . . 3.5 Operadores de Strings . . . . . . . . 3.6 Denio de Funes . . . . . . . . . 3.7 Smbolos . . . . . . . . . . . . . . . . 3.8 Avaliao de Smbolos . . . . . . . . 3.9 Funes de Mltiplos Parmetros . . 3.10 Encadeamento de Funes . . . . . . 3.11 Funes Pr-Denidas . . . . . . . . 3.12 Aritmtica de Inteiros em Auto Lisp 3.13 Aritmtica de Reais em Auto Lisp . 3.14 Smbolos e Avaliao . . . . . . . . . Combinao de Dados 4.1 Coordenadas . . . . . . . . . . . . . . 4.2 Pares . . . . . . . . . . . . . . . . . . 4.3 Operaes com Coordenadas . . . . 4.4 Abstraco de Dados . . . . . . . . . 4.5 Coordenadas Tri-Dimensionais . . . 4.6 Coordenadas Bi- e Tri-Dimensionais 4.7 A Notao de Lista . . . . . . . . . . 4.8 tomos . . . . . . . . . . . . . . . . . 4.9 Tipos Abstractos . . . . . . . . . . . . 4.10 Coordenadas em AutoCad . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7 10 11 12 13 14 14 15 16 16 17 18 20 22 23 24 27 28 29 30 30 33 36 38 40 40 41 43 44 47 51 54 56 57 59

4.11 4.12 4.13 4.14 4.15 4.16 4.17 4.18 4.19

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

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

60 63 71 72 74 75 79 84 88 88 90 92 92 97 99 101 101 101 102 102 103 103 104 104 105 106 107 108 109 110 112 114 116 118 119 122 124

Modelao Tridimensional 5.1 Slidos Tridimensionais Pr-Denidos . . . . . . . . . . . . . 5.2 Modelao de Colunas Dricas . . . . . . . . . . . . . . . . . Expresses Condicionais 6.1 Expresses Lgicas . . . . . . . . . . . . . . . . . 6.2 Valores Lgicos . . . . . . . . . . . . . . . . . . . 6.3 Predicados . . . . . . . . . . . . . . . . . . . . . . 6.4 Predicados Aritmticos . . . . . . . . . . . . . . . 6.5 Operadores Lgicos . . . . . . . . . . . . . . . . . 6.6 Predicados com nmero varivel de argumentos 6.7 Predicados sobre Cadeias de Caracteres . . . . . 6.8 Predicados sobre Smbolos . . . . . . . . . . . . . 6.9 Predicados sobre Listas . . . . . . . . . . . . . . . 6.10 Reconhecedores . . . . . . . . . . . . . . . . . . . 6.11 Reconhecedores Universais . . . . . . . . . . . . 6.12 Exerccios . . . . . . . . . . . . . . . . . . . . . . . Estruturas de Controle 7.1 Sequenciao . . . . . . . . . . . . 7.2 Invocao de Funes . . . . . . . . 7.3 Variveis Locais . . . . . . . . . . . 7.4 Atribuio . . . . . . . . . . . . . . 7.5 Variveis Globais . . . . . . . . . . 7.6 Variveis Indenidas . . . . . . . . 7.7 Propores de Vitrvio . . . . . . . 7.8 Seleco . . . . . . . . . . . . . . . 7.9 Seleco MltiplaA Forma cond 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7.10 7.11 7.12 7.13 7.14 7.15

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

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

126 130 135 138 139 146 147 151 163 171

Programao

A transmisso de conhecimento um dos problemas que desde cedo preocupou a humanidade. Sendo o homem capaz de acumular conhecimento ao longo de toda a sua vida, com desnimo que enfrenta a ideia de que, com a morte, todo esse conhecimento se perca. Para evitar esta perda, a humanidade inventou toda uma srie de mecanismos de transmisso de conhecimento. O primeiro, a transmisso oral, consiste na transmisso do conhecimento de uma pessoa para um grupo reduzido de outras pessoas, de certa forma transferindo o problema da perda de conhecimento para a gerao seguinte. O segundo, a transmisso escrita, consiste em registar em documentos o conhecimento que se pretende transmitir. Esta forma tem a grande vantagem de, por um lado, poder chegar a muitas mais pessoas e, por outro, de reduzir signicativamente 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 sucientes para conseguir ultrapassar os obstculos mas, no caso da transmisso de conhecimentos mais complexos j isso poder ser muito mais difcil. Infelizmente, quando se exige rigor na transmisso de conhecimento, fazer depender a compreenso desse conhecimento da capacidade de interpretao de quem o recebe pode ter consequncias desastrosas e, de facto, a histria da humanidade est repleta de acontecimentos catastrcos cuja causa , to somente, uma insuciente ou errnea transmisso de conhecimento. Para evitar estes problemas, inventaram-se linguagens mais rigorosas. A matemtica, em particular, tem-se obssessivamente preocupado ao longo dos ltimos milnios com a construo de uma linguagem onde o rigor seja 4

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

Neste caso, no s as reticncias deixam de fazer sentido como toda a frmula deixa de fazer sentido, o que mostra que, na verdade, a imaginao necessria para a interpretao da frmula original no se restringia apenas s reticncias mas sim a toda a frmula: o nmero de termos a considerar depende do nmero do qual queremos saber o factorial. Admitindo que o nosso leitor teria tido a imaginao suciente para descobrir esse detalhe, ele conseguiria tranquilamente calcular que 2! = 1 2 = 2. Ainda assim, casos haver que o deixaro signicativamente menos tranquilo. Por exemplo, qual o factorial de zero? A resposta no parece bvia. E quanto ao factorial de 1? Novamente, no est claro. E quanto ao factorial de 4.5? Mais uma vez, a frmula nada diz e a nossa imaginao tambm no consegue adivinhar. Ser possvel encontrar uma forma de transmitir o conhecimento da funo factorial que minimize as imprecises, lacunas e ambiguidades? Ser possvel reduzir ao mnimo razovel a imaginao necessria para compreender esse conhecimento? Experimentemos a seguinte variante da denio da funo factorial: n! = 1, n (n 1)!, se n = 0 se n N.

Teremos agora atingido o rigor suciente que dispensa imaginao por parte do leitor? A resposta estar na anlise dos casos que causaram problemas denio anterior. Em primeiro lugar, no existem reticncias, o que positivo. Em segundo lugar, para o factorial de 2 temos, pela denio: 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 denio 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 suciente para que a humanidade tenha conseguido preservar o conhecimento adquirido ao longo da sua histria. Acontece que, actualmente, a humanidade conta com um parceiro que tem dado uma contribuio gigantesca para o seu progresso: o computador. Esta mquina tem a extraordinria capacidade de poder ser instruda de forma a saber executar um conjunto complexo de tarefas. A actividade 6

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

1.1

Linguagens de Programao

Para que um computador possa resolver um problema necessrio que consigamos fazer uma descrio do processo de resoluo desse problema numa linguagem que o computador entenda. Infelizmente, a linguagem que os computadores entendem de forma inata extraordinariamente pobre, levando a que a qualquer problema no trivial acabe por exigir uma exaustiva, entediante e extremamente complexa descrio do processo de resoluo. As inmeras linguagens de programao que tm sido inventadas visam precisamente aliviar o programador desse fardo, introduzindo elementos lingusticos capazes de simplicar 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 cientcos. Isto implica, naturalmente, que tem de existir um processo capaz de transformar as descries que o programador faz em descries que o computador entenda. Embora este processo seja relativamente complexo, o que nos importa saber que ele permite termos linguagens de programao mais prximas das capacidades do pensamento humano do que das capacidades de pensamento do computador. Este ltimo facto tem uma importncia crucial pois permite que passemos a usar linguagens de programao, no s para instruir um computador sobre uma forma de resolver um problema, mas tambm para explicar rigorosamente esse processo a outros seres humanos. A linguagem de 7

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

1.2

Lisp

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

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

1.3

Exerccios

Exerccio 1.3.1 A exponenciao bn uma operao entre dois nmeros b e n designados base e expoente, respectivamente. Quando n um inteiro positivo, a exponenciao denese como uma multiplicao repetida: bn = b b b | {z }
n

Para um leitor que nunca tenha utilizado o operador de exponenciao, a denio 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 denio matemtica de exponenciao que no levante estas questes.

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

Sintaxe, Semntica e Pragmtica

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

2.1

Sintaxe e Semntica do Lisp

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

2.2

O Avaliador

Em Lisp, qualquer expresso tem um valor. Este conceito de tal modo importante que todas as implementaes da linguagem Lisp apresentam um avaliador, i.e., um programa destinado a interagir com o utilizador de modo a avaliar expresses por este fornecidas. No caso do AutoCad, o avaliador est contido num ambiente interactivo de desenvolvimento, denominado Visual Lisp e pode ser acedido atravs de menus (menu Tools, submenu AutoLISP, item Visual LISP Editor) ou atravs do comando vlisp. Uma vez acedido o Visual Lisp, o utilizador encontrar uma janela com o ttulo Visual LISP Console e que se destina precisamente interaco com o avaliador de Auto Lisp. Assim, quando o utilizador comea a trabalhar com o Lisp, -lhe apresentado um sinal (denominado prompt) e o Lisp ca espera que o utilizador lhe fornea uma expresso.
Originalmente, Lisp dizia-se baseado em S-expression, uma abreviatura para expresses simblicas. Esta caracterizao ainda aplicvel mas, por agora, no a vamos empregar.
1

11

_$

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

Elementos da Linguagem

Em qualquer linguagem de programao precisamos de lidar com duas espcies de objectos: dados e procedimentos. Os dados so as entidades que pretendemos manipular. Os procedimentos so descries das regras para manipular esses dados. Se considerarmos a linguagem da matemtica, podemos identicar 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 car eternamente a resolver problemas de aritmtica elementar, convm considerar operaes mais elaboradas que representam padres de clculos. Na sequncia de combinaes que apresentmos evidente que o padro que est a emergir o da operao de potenciao, i.e, multiplicao sucessiva, tendo esta operao sido denida 12

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

3.1

Elementos primitivos

Elementos primitivos so as entidades mais simples com que a linguagem lida. Os elementos primitivos podem ser divididos em dados primitivos e procedimentos primitivos. Um nmero, por exemplo, um dado primitivo. J a operao de adio de nmeros um procedimento primitivo. 3.1.1 Nmeros

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

Como se v no exemplo, em Lisp, os nmeros podem ser inteiros ou reais. Os reais so nmeros com um ponto decimal e, no caso do Auto Lisp, tem de haver pelo menos um dgito antes do ponto decimal:
_$ 0.1 0.1 _$ .1 ; error: misplaced dot on input

13

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

Os reais podem ser escritos em notao decimal ou cientca consoante o seu tamanho. 3.1.2 Cadeias de Caracteres

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

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

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

14

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

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

Tabela 1: Caracteres de escape vlidos em Auto Lisp

3.2

Combinaes

Uma combinao uma expresso que descreve a aplicao de um operador aos seus operandos. Por exemplo, na matemtica, os nmeros podem ser combinados usando operaes como a soma ou o produto. Como exemplo de combinaes matemticas, temos 1+2 e 1+2 3. 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 signicado pr-denido. 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 prexa por o operador ocorrer prviamente aos operandos. Esta notao costuma causar alguma perplexidade a quem inicia o estudo da linguagem, que espera uma notao mais prxima da que aprendeu em aritmtica e que usada habitualmente nas outras linguagens de programao. Nestas, a expresso (+ 1 (* 2 3)) usualmente escrita na forma 1 + 2 * 3 (designada no15

tao inxa, por o operador ocorrer entre os operandos) que, normalmente, mais simples de ler por um ser humano. No entanto, a notao prexa usada pelo Lisp tem vantagens sobre a notao inxa: 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 inxa, 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 denimos 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 inxos, i.e., esto entre os operandos, e os procedimentos por ns denidos so prexos, i.e., primeiro vem o nome do procedimento e depois os operandos. Isto impede a extenso da linguagem de uma forma coerente. Para exemplicar este ltimo aspecto, consideremos a operao de exponenciao numa linguagem com operadores inxos. 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 denir novos operadores inxos. A consequncia que o novo operador, por no se poder usar de forma inxa e ter de se usar de forma prexa, no ca coerente com as restantes operaes da linguagem, como a soma e a multiplicao. Em Lisp, pelo contrrio, tanto po2

Estes operadores dizem-se varidicos.

16

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

3.3

Indentao

A desvantagem da notao prexa 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)), ca 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 suciente de espaos em branco esquerda para que os operandos quem correctamente arrumados. No caso de uma expresso ser curta, podemos escrev-la numa s linha, com os operandos logo a seguir ao operador, com um espao em branco entre cada um. Usando estas duas regras, podemos reescrever a expresso anterior na seguinte forma, onde usamos linhas verticais imaginrias para salientar a indentao:
(- (+ 1 (* 2 3)) (* (/ 4 5) 6))

Note-se que o facto de se dispor uma combinao ao longo de vrias linhas em nada afecta a forma como o Lisp a l pois a correcta delimitao dos elementos da combinao feita apenas pela sua separao visual e pela utilizao de parnteses correctamente emparelhados. 17

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

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


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

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

18

7. 1 (2 + 3) 8. 2 2 + 3 3 3 Exerccio 3.3.4 Converta as seguintes expresses da notao prexa do Lisp para a notao inxa da aritmtica: 1. 2. 3. 4. 5. 6. 7. (* (* (/ (/ (/ (((/ 1 2) 3) 1 (- 2 3)) (+ 1 2) 3) (/ 1 2) 3) 1 (/ 2 3)) (- 1 2) 3) 1 2 3)

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

3.4

Avaliao de Combinaes

Como j vimos, o Lisp considera que o primeiro elemento de uma combinao um operador e os restantes so os operandos. O avaliador determina o valor de uma combinao como o resultado de aplicar o procedimento especicado 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 denio ligeiramente diferente para o operador de diviso: em Auto Lisp, o smbolo / representa a diviso inteira, i.e., o nmero que multiplicado pelo divisor e somado ao resto iguala o dividendo. Assim, (/ 7 2) avalia para 3. No entanto, quando est a lidar com nmeros reais, o Auto Lisp j faz a diviso segundo as regras da matemtica mas, possivelmente, envolvendo perdas de preciso. Assim, (/ 7.0 2) produz 3.5. 19

Exerccio 3.4.1 Calcule o valor das seguintes expresses Lisp: 1. (* (/ 1 2) 3) 2. (* 1 (- 2 3)) 3. (/ (+ 1 2) 3) 4. (- (- 1 2) 3) 5. (- 1 2 3) 6. (- 1)

3.5

Operadores de Strings

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

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


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

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

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

3.6

Denio de Funes

Para alm das operaes bsicas aritmticas, a matemtica disponibilizanos um vastssimo conjunto de outras operaes que se denem 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, dene-se o quadrado de um nmero pela funo x2 = x x. Tal como em matemtica, pode-se denir 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 denio da funo quadrado devolve como valor o nome da funo denida. Note-se ainda que o Auto Lisp escreve esse nome em maisculas. Na realidade, o que se passa que, por motivos histricos, todos os nomes que escrevemos no Auto Lisp so traduzidos para maisculas assim que so lidos. Por este motivo, quadrado, QUADRADO, Quadrado, qUaDrAdo, ou qualquer outra combinao de maisculas e minsculas designa o mesmo nome em Auto Lisp: QUADRADO. Apesar da converso para maisculas que feita na leitura, pragmtica usual escrevermos os nossos programas em letras minsculas. 21

Como se pode ver pela denio da funo quadrado, para se denirem 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 denir uma funo. O segundo elemento o nome da funo que queremos denir, 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 denio 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 denio o nome da funo denida. Se j existir uma funo com o mesmo nome, a denio 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 denio diz que para se determinar o quadrado de um nmero x, devemos multiplicar esse nmero por ele prprio (* x x). Esta denio 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 denidas. Assim, a avaliao da expresso (quadrado (+ 1 2)) passa pela avaliao do operando (+ 1 2). Este operando tem como valor 3, valor esse que usado pela funo no lugar do parmetro x. O corpo da funo ento avaliado mas substituindo

22

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

Em cada passo, uma das sub-expresses foi avaliada. (quadrado (quadrado (+ 1 2))) (quadrado (quadrado 3)) (quadrado (* 3 3)) (quadrado 9) (* 9 9) 81 Como dissemos, a denio 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 denies de procedimentos que tenhamos feito, convm regist-las num suporte persistente, como seja um cheiro. Para facilitar a utilizao, o Lisp permite que se avaliem as diversas denies a partir de um cheiro. Por este motivo, o processo usual de trabalho com Lisp consiste em escrever as vrias denies em cheiros embora se continue a usar o avaliador de Lisp para experimentar e testar o correcto funcionamento das nossas denies.
Exerccio 3.6.1 Dena a funo dobro que, dado um nmero, calcula o seu dobro.

23

3.7

Smbolos

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

24

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

3.8

Avaliao de Smbolos

Vimos que todos os outros elementos primitivos que apresentmos at agora, nomeadamente, os nmeros e as cadeias de caracteres avaliavam para eles prprios, i.e., o valor de uma expresso composta apenas por um elemento primitivo o prprio elemento primitivo. No caso dos smbolos, isso j no verdade. Lisp atribui um signicado muito especial aos smbolos. Reparemos que, quando denimos 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 denio 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 denido a funo quadrado tal como vimos na seco 3.6, podemos vericar este comportamento experimentando as seguintes expresses:
_$ (quadrado 3) 9 _$ quadrado #<USUBR @1d4c66f4 QUADRADO>

Como se pode ver pelo exemplo anterior, o valor do smbolo quadrado uma entidade que o Lisp descreve usando uma notao especial. A entidade em questo , como vimos, uma funo. O mesmo comportamento ocorre para qualquer outra funo pr-denida na linguagem:5
_$ + #<SUBR @0a87c5a8 +> _$ * #<SUBR @0a87c588 *> 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 eciente. O termo SUBR uma abreviatura de subroutine.
5

25

Como vimos com o + e o *, alguns dos smbolos esto pr-denidos na linguagem. Por exemplo, o smbolo PI tambm existe pr-denido com uma aproximao do valor de .
_$ pi 3.141519

No entanto, quando o avaliador est a avaliar o corpo de uma funo, o valor de um smbolo especicado 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 denida atrs e que o valor de 3 3, o avaliador passa a avaliar o corpo da funo quadrado mas assumindo que, durante essa avaliao, o nome x, sempre que for necessrio, ir ter como valor precisamente o mesmo 3 que foi associado ao parmetro x.
Exerccio 3.8.1 Dena a funo radianos<-graus que recebe uma quantidade angular em graus e calcula o valor correspondente em radianos. Note que 180 graus correspondem a radianos. Exerccio 3.8.2 Dena a funo graus<-radianos que recebe uma quantidade angular em radianos e calcula o valor correspondente em graus. Exerccio 3.8.3 Dena a funo que calcula o permetro de uma circunferncia de raio r.

3.9

Funes de Mltiplos Parmetros

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

Como se pode ver, a denio da funo em Lisp idntica denio correspondente em Matemtica, com a diferena de os operadores se usarem de forma prexa e o smbolo de denio matemtica = se escrever defun. No entanto, contrariamente ao que usual ocorrer em Matemtica, os nomes que empregamos em Lisp devem ter um signicado 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 denio mais legvel: 26

(defun area-triangulo (base altura) (/ (* base altura) 2))

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

3.10

Encadeamento de Funes

Todas funes por ns denidas so consideradas pelo avaliador de Lisp em p de igualdade com todas as outras denies. Isto permite que elas possam ser usadas para denir ainda outras funes. Por exemplo, aps termos denido a funo quadrado, podemos denir a funo que calcula a rea de um crculo de raio r atravs da frmula r2 .
(defun area-circulo (raio) (* pi (quadrado raio)))

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

3.11

Funes Pr-Denidas

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

de ser denidas custa de outras que, ou foram tambm por ns denidas ou, no limite, j estavam pr-denidas na linguagem. Isto mesmo se verica no caso da funo area-circulo que denimos acima: ela est denida custa da funo quadrado (que foi tambm por ns denida) e custa da operao de multiplicao. No caso da funo quadrado, ela foi denida 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-denida do Lisp. Como iremos ver, Lisp providencia um conjunto razoavelmente grande de funes pr-denidas. Em muitos casos, elas so sucientes para o que pretendemos mas, em geral, no nos devemos coibir de denir novas funes sempre que acharmos necessrio. A Tabela 2 apresenta uma seleco de funes matemticas pr-denidas 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 x escreve-se (fix x) (de xed point) e a funo potncia xy que se escreve (expt x y) (de exponential). A Tabela 3 mostra as equivalncias mais relevantes entre invocaes de funes em Auto Lisp e as correspondentes invocaes na Matemtica. A Tabela 4 apresenta uma seleco de funes sobre strings pr-denidas do Auto Lisp. medida que formos apresentando novos tpicos do Auto Lisp iremos explicando outras funes pr-denidas que sejam relevantes para o assunto.
Exerccio 3.11.1 Como pode vericar 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, armando que est errado e que o valor correcto deveria ser outro. Concorda? Em caso armativo, qual deveria ser o valor correcto? Exerccio 3.11.2 Embora o seno (sin) e o cosseno (cos) sejam funes pr-denidas em sin x Auto Lisp, a tangente (tan) no . Dena-a a partir da frmula tan x = cos . x Exerccio 3.11.3 Do conjunto das funes trigonomtricas inversas, o Auto Lisp apenas providencia o arco tangente (atan). Dena tambm as funes arco-seno (asin) e arco-cosseno

28

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

atan 3 1 2

3 + sin 2 2

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

29

Para simplicar a vida destas pessoas, pretende-se que implemente uma funo denominada requerimento que, convenientemente parameterizada, gera uma string com a frase apropriada para qualquer um dos ns anteriormente exemplicados e tambm para outros do mesmo gnero que possam vir a ser necessrios. Teste a sua funo para garantir que consegue reproduzir os dois exemplos anteriores passando o mnimo de informao nos argumentos da funo. Exerccio 3.11.10 Tambm na actividade da Arquitectura, usual os projectos necessitarem de informaes textuais escritas numa forma padronizada. Por exemplo, considere as seguintes trs frases contidas nos Cadernos de Encargos de trs diferentes projectos: Toda a caixilharia ser metlica, de alumnio anodizado, cor natural, construda com os pers 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 pers 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 pers standard existentes no mercado, sujeitos a aprovao da Fiscalizao. Dena uma funo que, convenientemente parameterizada, gera a string adequada para os trs projectos anteriores, tendo o cuidado de a generalizar para qualquer outra situao do mesmo gnero.

3.12

Aritmtica de Inteiros em Auto Lisp

Vimos que o Auto Lisp disponibiliza, para alm dos operadores aritmticos usuais, um conjunto de funes matemticas. No entanto, h que ter em conta que existem diferenas substanciais entre o signicado matemtico destas operaes e a sua implementao no Auto Lisp. Um primeiro problema importante tem a ver com a gama de inteiros. Em Auto Lisp, os inteiros so representados com apenas 32 bits de informao.6 Isto implica que apenas se conseguem representar inteiros desde 2147483647 at 2147483647. Para tornar a situao ainda mais problemtica, embora exista esta limitao, quando ela no respeitada o Auto Lisp no emite qualquer aviso. Este comportamento usual na maioria das linguagens de programao e est relacionado com questes de performance.7
Em AutoCad, a gama de inteiros ainda mais pequena pois a sua representao apenas usa 16 bits, permitindo apenas representar os inteiros de 32768 a 32767. Isto no afecta o Auto Lisp excepto quando este tem de passar inteiros para o AutoCad. 7 Curiosamente, a maioria dos dialectos de Lisp (mas no o Auto Lisp) no apresenta este comportamento, preferindo representar nmeros inteiros com dimenso to grande quanto for necessrio.
6

30


2147483648 2 1 0 +1 +2 1 0 +1

+
+2147483647

+2

2147483648

+2147483647

Figura 1: A recta innita dos inteiros empregue em aritmtica e o crculo dos inteiros modulares empregues em Auto Lisp. Infelizmente, a performance tem como preo o poder originar resultados aparentemente bizarros:
_$ (+ 2147483647 1) -2147483648

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

Finalmente, um terceiro problema que ocorre com os inteiros que a leitura de um nmero inteiro que excede a gama dos inteiros implica a sua 31

converso automatica para o tipo real.8 Neste caso, o avaliador do Auto Lisp nunca chega a ver o nmero inteiro pois ele foi logo convertido para real na leitura. Esse comportamento visvel no seguinte exemplo:
_$ 1234567890 1234567890 _$ 12345678901 1.23457e+010

Tambm este aspecto do Auto Lisp pode ser fonte de comportamentos bizarros, tal como demonstrado pelo seguinte exemplo:
_$ (+ 2147483646 1) 2147483647 _$ (+ 2147483647 1) -2147483648 _$ (+ 2147483648 1) 2.14748e+009

importante salientar que a converso de inteiro para real que visvel na ltima interaco feita logo na leitura.
Exerccio 3.12.1 Calcule o valor das seguintes expresses Auto Lisp: 1. (- 1) 2. (- 1 1) 3. (- 1 1 1) 4. (/ 1 1) 5. (/ 1 2) 6. (/ (* 3 2) 2) 7. (* (/ 3 2) 2) 8. (/ (/ (/ 8 2) 2) 2) 9. (/ 8 (/ 2 (/ 2 2))) 10. (+ (/ 1 2) (/ 1 2)) Exerccio 3.12.2 Explique o seguinte comportamento do Auto Lisp:9 Note-se que estamos apenas a falar da leitura de nmeros. Como j vimos, as operaes aritmticas usuais no apresentam este comportamento. 9 Este exemplo mostra que, em Auto Lisp, h um inteiro que no pode ser lido mas que pode ser produzido como resultado de operaes aritmticas. importante salientar que este comportamento verdadeiramente bizarro exclusivo do Auto Lisp e no se verica em mais nenhum dialecto de Lisp.
8

32

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

Exerccio 3.12.3 A rea A de um pentgono regular inscrito num crculo de raio r dada pela frmula q 5 A = r2 10 + 2 5 8 Dena uma funo Auto Lisp que calcule essa rea. Exerccio 3.12.4 Dena uma funo que calcula o volume de um elipside de semi-eixos a, 4 b e c. Esse volume pode ser obtido pela frmula V = 3 abc.

3.13

Aritmtica de Reais em Auto Lisp

Em relao aos reais, o seu comportamento mais prximo do que se considera matematicamente correcto mas, ainda assim, h vrios problemas com que preciso lidar. A gama dos reais vai desde 4.94066 10324 at 1.79769 10+308 . Se o Auto Lisp tentar ler um nmero real que excede esta gama ele imediatamente convertido para um nmero especial que representa o innito:
_$ 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 innito, 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 innito.
_$ 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 overow. 33

As operaes com nmeros reais tm a vantagem de a maioria dos computadores actuais conseguirem detectar o overow de reais e reagirem em conformidade (gerando um erro ou simplesmente produzindo uma representao do innito). Como vimos anteriormente, se se tivessem usado nmeros inteiros, ento o overow 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 ( 4 3 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 nito 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 sucientemente 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 nito de dgitos binrios e, consequentemente, ocorrem erros de arredondamento. Por este motivo, o resultado da diviso, em 34

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

3.14

Smbolos e Avaliao

Vimos que os smbolos avaliam para aquilo a que estiverem associados no momento da avaliao. Por este motivo, os smbolos so usados para dar nomes s coisas. O que mais interessante que os smbolos so, eles prprios, coisas! Para percebermos esta caracterstica dos smbolo vamos apresentar uma funo pr-denida do Auto Lisp que nos permite saber o tipo de uma entidade qualquer: type. Consideremos a seguinte interaco:
_$ (type INT _$ (type STR _$ (type REAL _$ (type USUBR _$ (type SUBR 1) "Bom dia!") pi) quadrado) +)

35

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

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

36

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

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

Combinao de Dados

Vimos, nas seces anteriores, alguns dos tipos de dados pr-denidos em Auto Lisp. Em muitos casos, esses tipos de dados so os necessrios e sucientes para nos permitir criar os nossos programas mas, noutros casos, ser necessrio introduzirmos novos tipos de dados. Nesta seco iremos estudar o modo de o fazermos e iremos exemplic-lo com um tipo de dados que nos ser particularmente til: coordenadas.

4.1

Coordenadas

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

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

4.2

Pares

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

Como podemos ver pela interaco anterior, quando o Lisp pretende escrever o resultado da criao de um par, ele comea por escrever um
O cons para o Lisp o mesmo que as tabelas (arrays) e estruturas (records, structs) so para as outras linguagens como Pascal ou C. Na prtica, o cons um mecanismo de agregao de entidades.
10

38

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

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

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

39

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

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

4.3

Operaes com Coordenadas

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

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

y1 d y0 P0 x0

P1

x1

Figura 3: Distncia entre dois pontos. A aplicao do teorema de Pitgoras permite-nos determinar a distncia d em termos das coordenadas dos pontos P0 a P1 : d= (x1 x0 )2 + (y1 y0 )2

A traduo desta frmula para Auto Lisp consiste da seguinte denio:


(defun distancia-2d (p0 p1) (sqrt (+ (quadrado (- (car p1) (car p0))) (quadrado (- (cdr p1) (cdr p0))))))

Podemos agora experimentar a funo com um caso concreto:


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

4.4

Abstraco de Dados

Embora tenhamos pensado na operao distancia-2d como uma funo que recebe as coordenadas de dois pontos, quer quando observamos a denio da funo, quer quando observamos a sua utilizao subsequente, o que vemos a invocao das operaes cons, car e cdr e, consequentemente, nada nos diz que a funo esteja a lidar com coordenadas. Embora o conceito de coordenadas esteja presente nos nossos pensamentos, os nossos programas apenas mostram que estamos a construir e a manipular pares.

41

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

A construo de coordenadas bi-dimensionais por intermdio da funo xy apenas o primeiro passo para abstrairmos a utilizao dos pares. O segundo passo a criao de funes que acedem abcissa x e ordenada y das coordenadas (x, y ). Para isso, vamos denir, respectivamente,
Naturalmente, podemos considerar outro nome igualmente apropriado como, por exemplo, coordenadas-cartesianas-2d. Contudo, como de esperar que tenhamos frequentemente de criar coordenadas, conveniente que adoptemos um nome sucientemente curto e, por isso, vamos adoptar o nome xy.
11

42

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


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

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

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

A introduo das operaes de coordenadas xy, cx e cy no s torna mais claro o signicado dos nossos programas como facilita bastante a denio de novas funes. Agora, ao invs de termos de nos lembrar que as coordenadas so pares cujo primeiro elemento a abcissa e cujo segundo elemento a ordenada, basta-nos pensar nas operaes bsicas de coordenadas e denir as funes que pretendermos em termos delas. Por exemplo, imaginemos que pretendemos denir uma operao que faz deslocar um ponto de uma determinada distncia, expressa em termos de um comprimento x e de uma altura y , tal como est apresentado na Figura 4. Como ser evidente pela Figura, sendo P = (x, y ), ento teremos P = (x + x , y + y ). Para simplicar o uso desta funo, vamos
Tal como a funo xy poderia ter um nome mais explcito, tambm as funes que acedem abcissa e ordenada se poderiam denominar abcissa e ordenada ou coordenada-x e coordenada-y ou qualquer outro par de nomes sucientemente claro. No entanto, sendo as coordenadas um tipo de dados com muito uso, mais uma vez se tornar vantajoso que empreguemos nomes curtos.
12

43

P y P x

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

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

4.5

Coordenadas Tri-Dimensionais

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

x y P

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

Uma vez que um triplo de nmeros se pode implementar custa de dois pares, vamos usar os mesmos dois pares para implementar coordenadas tri-dimensionais. Para abstrair a utilizao destes pares, vamos denir a funo xyz para construir coordenadas tri-dimensionais e vamos denir as funes cx, cy e cz para aceder s coordenadas x, y e z , respectivamente. Comecemos pela funo xyz:
(defun xyz (x y z) (cons (cons x y) z))

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

z x y

Como evidente, qualquer outro arranjo de pares que juntasse os trs elementos seria igualmente vlido. A partir do momento em que temos a funo xyz que constri as coordenadas, estamos em condies de denir as funes que acedem s suas componentes:
(defun cx (c) (car (car c))) (defun cy (c) (cdr (car c))) (defun cz (c) (cdr c))

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

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

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

Traduzindo est denio para Auto Lisp, temos:


(defun distancia-3d (p0 (sqrt (+ (quadrado ((quadrado ((quadrado (p1) (cx p1) (cx p0))) (cy p1) (cy p0))) (cz p1) (cz p0))))))

semelhana do que zemos para as coordenadas bi-dimensionais, tambm podemos denir operadores para incrementar as coordenadas tri47

dimensionais de um ponto de uma determinada distncia especicada em termos das projeces x , y e z :


(defun +xyz (p dx dy dz) (xyz (+ (cx p) dx) (+ (cy p) dy) (+ (cz p) dz)))

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

4.6

Coordenadas Bi- e Tri-Dimensionais

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

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


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

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

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

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

x y ?

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

49

x y nil

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


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

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

x y z nil

Obviamente, precisamos de redenir a funo xyz:


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

fcil constatarmos que as duas estruturas anteriores so perfeitamente compatveis no sentido de podermos ter uma s verso das operaes cx e cy que funcione quer para coordenadas bi-dimensionais, quer para coordenadas tri-dimensionais. De acordo com a notao de caixa e ponteiro apresentada, podemos denir:
Embora, do ponto de vista da Arquitectura clssica, no seja necessrio trabalhar com mais do que trs coordenadas, as modernas ferramentas de visualizao podem necessitar de trabalhar com coordenadas tetra-dimensionais, por exemplo, para lidarem com o tempo.
13

50

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

importante referirmos que esta redenio das funes no perturba as funes j denidas +xy, distancia-2d e distancia-3d. De facto, desde que se mantenha a consistncia entre as funes que constriem as coordenadas e as que seleccionam os seus componentes, nada ser afectado.

4.7

A Notao de Lista

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

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

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

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

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

52

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

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

4.8

tomos

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

53

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

4.9

Tipos Abstractos

A utilizao das operaes xyz, cx, cy e cz permite-nos denir um novo tipo de dadosas coordenadas cartesianas tri-dimensionaise, mais importante, permite-nos abstrair a implementao desse tipo de dados, i.e., esquecer o arranjo particular de pares que o implementa. Por este motivo, denomina-se este novo tipo de dados por tipo abstracto. Ele abstracto porque apenas existe no nosso pensamento. Para o Lisp, como vimos, as coordenadas so apenas arranjos particulares de pares. Esse arranjo particular denomina-se representao dos elementos do tipo e, como vimos quando
Na realidade, alguns dialectos de Lisp, incluindo o Auto Lisp, consideram que possvel aplicar as funes car e cdr lista vazia, obtendo-se, em ambos os casos, a lista vazia. 15 Nesta matria, a documentao do Auto Lisp est simplesmente incorrecta pois arma que tudo o que no for uma lista um tomo, esquecendo-se de excluir a lista vazia que, obviamente, uma lista mas tambm um tomo.
14

54

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

Dena uma funo denominada distancia-ponto-recta que, dadas as coordenadas dos pontos P0 , P1 e P2 , devolve a distncia mnima de P0 recta denida por P1 e P2 .

55

4.10

Coordenadas em AutoCad

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

56

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

4.11

Coordenadas Polares

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

57

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

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

Eis alguns exemplos do uso destas funes:17


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

58

P P

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


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

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

Como exemplos de utilizao, temos:


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

Embora a funo +pol funcione perfeitamente, a sua denio no to simples quanto poderia ser. Isso evidente quando a comparamos com a da funo +xy (que repetimos de seguida): 59

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

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

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

Uma vez que as coordenadas polares esto representadas em termos de coordenadas rectangulares, mesmo quando especicamos coordenadas polares a operao +c continua a funcionar correctamente.

4.12

A funo command

A partir do momento em que sabemos construir coordenadas passamos a poder utilizar um conjunto muito vasto de operaes grcas do Auto Lisp. 60

P P0 x x Figura 8: O deslocamento de um ponto por adio de um vector. Existem trs maneiras diferentes de se invocar essas operaes grcas mas, por agora, vamos explorar apenas a mais simples: a funo command. A funo command aceita um nmero arbitrrio de expresses que vai avaliando e passando os valores obtidos para o AutoCad medida que o AutoCad os vai requerendo.18 No caso de utilizao mais comum, a funo command recebe uma cadeia de caracteres que descreve o comando AutoCad que se pretende executar seguido de qualquer nmero de argumentos que sero usados como os dados necessrios para a execuo desse comando. A vantagem da funo command permitir criar programas completos que criam entidades grcas 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
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.
18

P1 y

61

que cria este crculo a seguinte:


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

Como se pode ver pelo exemplo, as coordenadas do centro do crculo foram especicadas atravs da criao de uma lista. No entanto, como referimos na seco 4.10, vamos evitar a especicao de coordenadas directamente em termos da sua representao como listas e vamos, em seu lugar, usar as operaes do tipo. Assim, a expresso anterior ca 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, nalmente, uma cadeia de caracteres com o texto a inserir. Eis um exemplo:
_$ (command "text" (xy 1 1) 1 0 "AutoCad") nil

O resultado da avaliao das duas invocaes anteriores visvel da Figura 9. Finalmente, a funo command pode ser usada para alterar os parmetros de funcionamento do AutoCad. Por exemplo, para desligar os Object Snaps podemos fazer:
_$ (command "osnap" "off") nil

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

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

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

Figura 10: Crculos e textos especicados usando coordenadas polares. o comando erase do AutoCad, o que podemos fazer escrevendo as letras e-r-a-s-e e premindo enter no nal. 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 ca 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 especicar, numa string o premir da tecla enter, a funo command assume que, aps passar todos os caracteres de uma string ao AutoCad, deve passar-lhe tambm o correspondente premir da tecla enter. Tentemos agora, luz desta explicao, construir a invocao da funo command que simula a interaco anterior. O primeiro passo da avaliao da expresso , como referimos, o passar da sequncia de letras e-r-a-s-e seguidas do premir virtual da tecla enter, o que podemos fazer escrevendo:

64

(command "erase" ...)

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

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

Assim, a string vazia, quando usada como argumento de command, equivalente a premir a tecla enter na linha de comandos do AutoCad. claro que, se na sequncia de uma invocao da funo command, o AutoCad car 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 sucientes ou no para o AutoCad conseguir completar o pretendido. Se no forem, o AutoCad limita-se a continuar espera que lhe forneamos, via command ou directamente na interface, os dados que lhe faltam, o que nos permite implementar um processo de colaborao com o AutoCad: parte do que se pretende feito no lado do Auto Lisp e a parte restante no lado do AutoCad. Por exemplo, imaginemos que pretendemos criar um crculo num dado ponto mas queremos deixar ao utilizador a escolha do raio. Uma soluo 65

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

e o utilizador iria ento ao AutoCad terminar a criao do crculo atravs da indicao do raio. Um problema um pouco mais complicado ser criar um crculo de raio xo mas deixar ao utilizador a escolha do centro. A complicao deriva de no conhecermos as coordenadas do centro mas querermos passar o raio: se nos falta o argumento do meio, ou seja, a posio do centro do crculo, no podemos passar o do m, 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 especicado pelo utilizador. Note-se que, tal como referido na Tabela 1, o carcter \ um carcter de escape e por isso que tem de ser escrito em duplicado.
_$ pause "\\" _$ (command "circle" pause 3) nil

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

66

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

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

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

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

67

4.13

Variantes de Comandos

Quando fornecemos um nome na linha de comando do AutoCad, ele tenta perceber o que pedido seguindo um algoritmo que involve os seguintes passos:19 1. O nome pesquisado na lista de comandos do AutoCad. Se o nome est na lista, ento, se o nome precedido de um ponto ., executa o comando e termina, caso contrrio pesquisa o nome na lista de comandos no-denidos20 e, se no o encontrar, executa o comando e termina.21 2. O nome pesquisado na lista de comandos externos denidos no cheiro de parmetros acad.pgp. Se o encontrar, executa o comando e termina. 3. O nome pesquisado na lista de comandos denidos pelo Auto Lisp. Se o encontrar ento, primeiro, verica se um comando de carregamento automtico e, se for, o programa correspondente carregado e, segundo, executa o comando e termina. 4. O nome pesquisado na lista de sinnimos (aliases) denidos no cheiro de parmetros do programa. Se o encontrar, substitui o nome pela sua expanso e volta ao princpio. 5. Termina com uma mensagem de erro indicando que o nome desconhecido. Repare-se que o algoritmo anterior fala de comandos cujo nome precedido de um ponto, de comandos no denidos, comandos externos, etc. Todas estas variedades existem porque, na realidade, possvel redenir ou tornar como no denidos os comandos pr-denidos do AutoCad. Isto implica que quando invocamos um comando como, por exemplo, circle, estamos na realidade a invocar a mais recente denio que foi feita para esse comando. S no caso de no ter sido feita qualquer redenio do comando e de este no ter sido tornado no denido que iremos invocar
Por motivos pedaggicos, esta descrio uma simplicao da realidade. Um comando no-denido no a mesma coisa que um comando indenido. O primeiro corresponde a um comando conhecido que foi explicitamente declarado como no denido enquanto o segundo corresponde a um comando totalmente desconhecido. 21 Este comportamento permite que se possam tornar comandos como no-denidos e, ainda assim, pode execut-los desde que se preceda o seu nome de um ponto.
20 19

68

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

4.14

ngulos em Comandos

Alguns dos comandos do AutoCad necessitam de saber raios e ngulos em simultneo. Por exemplo, para criar um polgono regular, o comando

69

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

Usando esta funo agora trivial criarmos polgonos com diferentes ngulos de rotao. Por exemplo, a seguinte sequncia de comandos produz a imagem representada na Figura 11:
(command "_.polygon" 3 (xy 0 0) "_Inscribed" (command "_.polygon" 3 (xy 0 0) "_Inscribed" (command "_.polygon" 4 (xy 3 0) "_Inscribed" (command "_.polygon" 4 (xy 3 0) "_Inscribed" (command "_.polygon" 5 (xy 6 0) "_Inscribed" (command "_.polygon" 5 (xy 6 0) "_Inscribed" (raio&angulo 1 0)) (raio&angulo 1 (/ pi 3))) (raio&angulo 1 0)) (raio&angulo 1 (/ pi 4))) (raio&angulo 1 0)) (raio&angulo 1 (/ pi 5)))

70

Figura 11: Tringulos, quadrados e pentgonos sobrepostos com diferentes ngulos de rotao.

4.15

Efeitos Secundrios

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

No entanto, na realidade, a expresso anterior devolveu, de facto, um valor: um smbolo cuja representao externa no escrita no terceiro passo do ciclo read-eval-print. Embora

71

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

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

4.16

A Ordem Drica

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

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

72

Figura 12: O Templo Grego de Segesta, exemplicando alguns aspectos da Ordem Drica. Este templo nunca chegou a ser terminado, sendo visvel, por exemplo, a falta das caneluras nas colunas. Fotograa de Enzo De Martino.

73

(1, 11) (1, 10.5) (0.8, 10)

(1, 11) (1, 10.5) (0.8, 10)

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

Figura 13: Uma coluna Drica de referncia. Nesta seco vamos esquematizar o desenho de uma coluna Drica (sem caneluras). Do mesmo modo que uma coluna Drica se pode decompor nos seus componentes fundamentaiso fuste, o coxim e o baco tambm o desenho da coluna se poder decompor no desenho dos seus componentes. Assim, vamos denir funes para desenhar o fuste, o coxim e o baco. A Figura 13 apresenta um modelo de referncia. Comecemos por denir 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 m da sequncia de pontos usa-se uma string vazia. Naturalmente, a invocao da funo fuste ter, como efeito secundrio, a criao do fuste da coluna na rea de desenho do AutoCad. Uma outra possibilidade, eventualmente mais correcta, seria pedir ao AutoCad a criao de uma linha fechada, algo que podemos fazer com a 74

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


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

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

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

Finalmente, vamos denir uma funo que desenha as trs partes da coluna:
24 Outras diferenas incluem o facto de as plines poderem ter espessura e estarem limitadas a um plano.

75

Figura 14: Uma coluna drica desenhada pelo AutoCad.


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

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

4.17

Parameterizao de Figuras Geomtricas

Infelizmente, a coluna que crimos na seco anterior tem todas as suas dimenses xas, pelo que ser difcil encontrarmos outras situaes em que possamos reutilizar a funo que denimos. Naturalmente, esta funo seria mais til se a criao da coluna fosse parameterizvel, i.e., se a criao 76

P1 = (x rt , y + a)

rt

P2 = (x + rt , y + a)

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

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

77

P2 = (x rt , y + a)

rt P = (x, y )

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

P1 = (x rb , y )

rb

Figura 16: Esquema do desenho do coxim de uma coluna. P4 , pontos esses que conseguimos calcular facilmente a partir do ponto P . Desta forma, estamos em condies de denir a funo que desenha o fuste. Para tornar mais claro o programa, vamos empregar os nomes a-fuste, r-base e r-topo para caracterizar a altura a, o raio da base rb e o raio do topo rt , respectivamente. A denio ca 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 especicar o desenho do coxim. Mais uma vez, convm pensarmos num esquema geomtrico, tal como apresentamos na Figura 16. Tal como no caso do fuste, a partir de um ponto P correspondente ao centro da base do coxim, podemos computar as coordenadas dos pontos que esto nas extremidades dos segmentos de recta que delimitam o desenho do coxim. Usando estes pontos, a denio da funo ca 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 denir o desenho do baco. Para isso, fazemos um novo esquema que apresentamos na Figura 17.

78

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

P = (x, y )
l P1 = (x 2 , y)

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

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

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

la rbc aa ac

af

rbf

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

80

Figura 19: Variaes de colunas dricas.


(coluna (coluna (coluna (coluna (coluna (coluna (xy 0 0) (xy 3 0) (xy 6 0) (xy 9 0) (xy 12 0) (xy 15 0) 9 7 9 8 5 6 0.5 0.5 0.7 0.4 0.5 0.8 0.4 0.4 0.5 0.3 0.4 0.3 0.3 0.6 0.3 0.2 0.3 0.2 0.3 0.6 0.2 0.3 0.1 0.4 1.0) 1.6) 1.2) 1.0) 1.0) 1.4)

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

4.18

Documentao

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

81

lunas dricas ca com o seguinte aspecto:25


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

O exemplo destina-se a mostrar as diferentes formas de documentao usadas em Lisp e no a mostrar um exemplo tpico de programa documentado. Na verdade, o programa to simples que no deveria necessitar de tanta documentao.

25

82

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

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

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

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

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

N
da extremidade

84

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

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

0 4 6 0 5 8

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

4 2 5 5 3 3

3 3 3 4 4 6

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

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

4.19

Depurao

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

Os erros sintticos ocorrem quando escrevemos frases que no obedecem gramtica da linguagem. Como exemplo prtico, imaginemos que pretendiamos denir uma funo que criava um nica coluna drica, que iremos 85

designar de coluna standard e que tem sempre as mesmas dimenses, no necessitando de quaisquer outros parmetros. Uma possibilidade para a denio 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 denio, o Auto Lisp ir apresentar um erro, avisando-nos de que algo est errado:26
; error: bad DEFUN syntax: (COLUNA-STANDARD (COLUNA (XY 0 0) 9 0.5 0.4 0.3 0.3 0.5))

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

O nosso erro agora bvio: esquecemo-nos da lista de parmetros. Se a funo no tem parmetros a lista de parmetros vazia mas tem de estar l na mesma. Uma vez que no estava, o Auto Lisp detecta e reporta um erro sinttico, uma frase que no obedece sintaxe da linguagem. H vrios outros tipos de erros sintticos que o Auto Lisp capaz de detectar e que sero apresentados medida que os formos discutindo. O importante, no entanto, no saber quais so os erros sintticos detectveis pelo Auto Lisp, mas antes saber que o Auto Lisp capaz de vericar 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 vericao para uma seleco ou para todo o cheiro 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 cheiro actual e escreve, numa janela parte, todos os erros sintticos encontrados. Nesse janela, se clicarmos duas vezes sobre uma mensagem de erro, somos conduzidos ao local do nosso programa onde o erro ocorre.
Nem todos os dialectos e interpretadores de Lisp possuem exactamente este comportamento mas, variaes parte, os conceitos so os mesmos.
26

86

4.19.2

Erros Semnticos

Os erros semnticos so muito diferentes dos sintticos. Um erro semntico no um erro na escrita de uma frase da linguagem mas sim um erro no signicado dessa frase. Dito de outra forma, um erro semntico ocorre quando escrevemos uma frase que julgamos ter um signicado 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 signicado 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 sucientemente 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 modicao funo coluna-standard que corrige a falta de parmetros mas que introduz um outro erro:27
(defun coluna-standard () (coluna (xy O 0) 9 0.5 0.4 0.3 0.3 0.5))

Do ponto de vista sinttico, a funo est correcta. No entanto, quando a invocamos surge um erro:
_$ (coluna-standard) ; error: bad argument type: numberp: nil

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

Consegue detect-lo?

87

Para se perceber melhor onde est o erro, o Visual Lisp providencia algumas operaes extremamente teis. Uma delas d pelo nome de Error Trace, est disponvel no menu View e destina-se a mostra a cascata de invocaes que acabou por provocar o erro.28 Quando executamos essa operao, o Visual Lisp apresenta-nos, numa pequena janela, a seguinte informao:
<1> [2] [3] [4] [5] [6] ... :ERROR-BREAK (+ nil -0.3) (+XY (nil 0) -0.3 9) (FUSTE (nil 0) 9 0.5 0.3) (COLUNA (nil 0) 9 0.5 0.4 0.3 0.3 0.5) (COLUNA-STANDARD)

Na informao acima, as reticncias representam outras invocaes que no so relevantes para o nosso problema e que correspondem s funes Auto Lisp cuja execuo antecede a das nossas funes.29 A listagem apresentada pelo Visual Lisp mostra, por ordem inversa, as invocaes de funes que provocaram o erro. Para cada linha, o Visual Lisp disponibiliza um menu contextual que, entre outras operaes, permite visualizarmos imediatamente no editor qual a linha do programa em questo. A leitura da listagem do error trace diz-nos que o erro foi provocado pela tentativa de somarmos nil a um nmero. Essa tentativa foi feita pela funo +xy que foi invocada pela funo fuste que foi invocada pela funo coluna que, nalmente, 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 identicar o erro: ele tem de estar na prpria funo coluna-standard e, mais especicamente, 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.
A expresso error trace pode ser traduzida para Rastreio de erros. Essas funes que foram invocadas antes das nossas revelam que, na verdade, parte da funcionalidade do Visual Lisp est ela prpria implementada em Auto Lisp.
29 28

88

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

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

Modelao Tridimensional

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

5.1

Slidos Tridimensionais Pr-Denidos

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

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

Note-se que alguns dos slidos, nomeadamente o paralelippedo e a cunha s podem ser construdos com a base paralela ao plano XY . Esta no uma verdadeira limitao do AutoCad pois possvel alterar a orientao do plano XY atravs da manipulao do sistema de coordenadas UCSuser coordinate system. A partir dos comandos disponibilizados pelo AutoCad possvel denir funes Auto Lisp que simpliquem 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, especicando 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,
Para se conhecer as especicidades de cada comando recomenda-se a consulta da documentao que acompanha o AutoCad. 31 A construo de uma pirmide exige a especicao simultnea do raio e ngulo da base. Tal como explicado na seco 4.14, a funo raio&angulo constri essa especicao a partir dos valores do raio e do ngulo.
30

90

Figura 20: Slidos primitivos em AutoCad. cada uma empregando diferentes parmetros, podemos considerar que os nicos parmetros relevantes so o centro da base, o raio da base e o centro do topo. Estes parmetros so sucientes para especicar 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 denir outras estruturas mais complexas. Por exemplo, uma cruz papal dene-se pela unio de trs cilindros horizontais de comprimento progressivamente decrescente dispostos ao longo de um cilindro vertical, tal como se pode ver na Figura 21. de salientar que os cilindros tm todos o mesmo raio e que o seu comprimento e posicionamento funo desse raio. Em termos de proporo, o cilindro vertical da cruz papal tem um comprimento igual a 20 raios, enquanto que os cilindros horizontais possuem comprimentos iguais a 14, 10 e 6 raios 91

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

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

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

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

cuja avaliao produz a imagem seguinte:

93

5.2

Modelao de Colunas Dricas

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

das operaes de modelao de slidos do AutoCad permite vrios modos diferentes de invocao. No caso da operao de modelao de troncos de coneconeo modo que nos mais conveniente aquele em que a operao recebe as coordenadas do centro da base do cone e o raio dessa base e, de seguida, especicamos o raio do topo do cone (com a opo Top radius) e, nalmente, a altura do tronco. Tendo isto em conta, podemos redenir 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 car 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 especicarmos. A mais directa consiste em indicar os dois cantos do paralelippedo. Uma outra, menos directa, consiste em indicar o centro do paralelippedo seguido do tamanho dos seus lados. Por agora, vamos seguir a mais directa:
(defun abaco (p a-abaco l-abaco) (command "_.box" (+xyz p (/ l-abaco -2.0) (/ l-abaco -2.0) 0) (+xyz p (/ l-abaco +2.0) (/ l-abaco +2.0) a-abaco))) Exerccio 5.2.1 Implemente a funo abaco mas empregando a criao de um paralelippedo centrado num ponto seguido da especicao do seu comprimento, largura e altura.

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

95

Figura 23: Modelao tri-dimensional das variaes de colunas dricas. Com estas redenies, podemos agora repetir as colunas que desenhmos na seco 4.17 e que apresentmos na Figura 19, mas, agora, gerando uma imagem tri-dimensional dessas mesmas colunas, tal como apresentamos na Figura 23:
(coluna (coluna (coluna (coluna (coluna (coluna (xyz 0 0 (xyz 3 0 (xyz 6 0 (xyz 9 0 (xyz 12 0 (xyz 15 0 0) 0) 0) 0) 0) 0) 9 7 9 8 5 6 0.5 0.5 0.7 0.4 0.5 0.8 0.4 0.4 0.5 0.3 0.4 0.3 0.3 0.6 0.3 0.2 0.3 0.2 0.3 0.6 0.2 0.3 0.1 0.4 1.0) 1.6) 1.2) 1.0) 1.0) 1.4)

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

Expresses Condicionais

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

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

97

6.1

Expresses Lgicas

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

6.2

Valores Lgicos

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

6.3

Predicados

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

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

98

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

6.4

Predicados Aritmticos

Os operadores relacionais matemticos <, >, =, , e = 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 prexa e escrevem-se, respectivamente, <, >, =, <=, >= e /=. Eis alguns exemplos:
_$ (> 4 3) t _$ (< 4 3) nil _$ (<= (+ 2 3) (- 6 1)) t

6.5

Operadores Lgicos

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

99

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

6.6

Predicados com nmero varivel de argumentos

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

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

6.7

Predicados sobre Cadeias de Caracteres

Na verdade, os operadores <, >, =, <=, >= e /= no se limitam a operarem sobre nmeros: eles aceitam tambm cadeias de caracteres como argumentos, fazendo uma comparao lexicogrca: os argumentos so comparados
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.
33

100

carcter a carcter enquanto forem iguais. Quando so diferentes, a ordem lxicograca 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") "pois" "poisar") "pois" "poisar") "abcd" "abce")

6.8

Predicados sobre Smbolos

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


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

Nenhum dos outros operadores relacionais aplicvel a smbolos.

6.9

Predicados sobre Listas

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

101

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

A funo atom permite-nos, assim, saber quais os tipos de entidades a que podemos aplicar as operaes car e cdr: apenas aquelas que no so tomos.

6.10

Reconhecedores

Para alm dos operadores relacionais, existem muitos outros predicados em Lisp, como por exemplo o zerop que testa se um nmero zero:
_$ (zerop 1) nil _$ (zerop 0) t

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

102

Para se perceberem as duas ltimas expresses do exemplo anterior convm recordar que nil representa no s a falsidade mas tambm a lista vazia.

6.11

Reconhecedores Universais

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

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

Note-se, no ltimo exemplo, que o reconhecedor universal listp tambm considera como lista um mero par de elementos. Na realidade, este reconhecedor devolve verdade sempre que o seu argumento um dotted pair ou o nil. As listas, como vimos, no so mais do que arranjos particulares de dotted pairs ou, no limite, uma lista vazia.
tradicional, em vrios dialectos de Lisp, terminar o nome dos predicados com a letra p. Noutros dialectos, prefere-se terminar esse nome com um ponto de interrogao precisamente porque, na prtica, a invocao de um predicado corresponde a uma pergunta que fazemos. Neste texto, nos casos em que zer sentido enquadrarmo-nos com a tradio, iremos empregar a letra p; em todos os outros casos iremos empregar o ponto de interrogao.
34

103

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

Obviamente, nada nos impede de denir os reconhecedores universais que pretendermos. semelhana do predicado numberp podemos denir os seus subcasos integerp e realp:
(defun integerp (obj) (= (type obj) int)) (defun realp (obj) (= (type obj) real))

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

6.12

Exerccios

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

104

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

Estruturas de Controle

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

7.1

Sequenciao

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

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

106

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

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

7.2

Invocao de Funes

A invocao de uma funo a estrutura de controle mais usada em Lisp. Para se invocar uma funo, necessrio construir uma combinao cujo primeiro elemento seja uma expresso que avalia para a funo que se pretende invocar e cujos restantes elementos so expresses que avaliam para os argumentos que se pretende passar funo. O resultado da avaliao da combinao o valor calculado pela funo para aqueles argumentos. A avaliao de uma combinao deste gnero processa-se nos seguintes passos: 1. Todos os elementos da combinao so avaliados, sendo que o valor do primeiro elemento necessariamente uma funo. 2. Associam-se os parmetros formais dessa funo aos argumentos, i.e., aos valores dos restantes elementos da combinao. Cada parmetro associado a um argumento, de acordo com a ordem dos parmetros e argumentos. gerado um erro sempre que o nmero de parmetros no igual ao nmero de argumentos. 3. Avalia-se o corpo da funo tendo em conta estas associaes entre os parmetros e os argumentos.
O nome progn parece pouco intuitivo mas, como a maioria dos nomes usados em Lisp, tem uma histria que o justica. Os Lisps originais disponibilizavam um operador denominado prog (abreviatura de program) que, entre vrias outras coisas, permitia sequenciao. Para alm deste operador, foram introduzidas variantes mais simples, denominadas prog1que avaliava sequencialmente os seus argumentos e retornava o valor da primeira, prog2que avaliava sequencialmente os seus argumentos e retornava o valor da segundae, nalmente, prognque avaliava sequencialmente os seus argumentos e retornava o valor da ltima. Apenas este ltimo operador foi implementado em Auto Lisp.
35

107

Para exemplicar, consideremos a denio da funo quadrado e a seguinte combinao:


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

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

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

7.3

Variveis Locais

Consideremos o seguinte tringulo

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

em que s o semi-permetro do tringulo: s=


36

a+b+c 2

Heron de Alexandria foi um importante matemtico e engenheiro Grego do primeiro sculo depois de Cristo a quem se atribuem inmeras descobertas e invenes, incluindo a mquina a vapor e a seringa.

109

Ao tentarmos usar a frmula de Heron para denir a correspondente funo Auto Lisp, somos confrontados com uma diculdade: 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 signicado: A= a+b+c 2 a+b+c a 2 a+b+c b 2 a+b+c c 2

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

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

(defun nome (parmetro1 ... parmetron / varivel1 ... varivelm ) corpo)

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

7.4

Atribuio

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

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

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

H dois detalhes que importante conhecer relativamente s variveis locais. O primeiro detalhe, que iremos discutir mais em profundidade na prxima seco, que se nos esquecermos de declarar a varivel, ela ser tratada como varivel global. O segundo detalhe que se nos esquecermos de atribuir a varivel ela ca automaticamente com o valor nil. Logicamente, para que uma varivel nos seja til, devemos mudar o seu valor para algo que nos interesse. Para visualizarmos um exemplo mais complexo, consideremos a equao do segundo grau ax2 + bx + c = 0 Como sabemos, esta equao tem duas razes que se podem obter pela famosa frmula resolvente: b + b2 4ac b b2 4ac x= x= 2a 2a Dada a utilidade desta frmula, podemos estar interessados em denir uma funo que, dados os coecientes a, b e c da equao do segundo grau nos devolve as suas duas raizes. Para isso, vamos devolver um par de nmeros calculados usando as duas frmulas acima:
(defun raizes-equacao-segundo-grau (a b c) (cons (/ (- (- b) (sqrt (- (quadrado b) (* 4 a c)))) (* 2 a)) (/ (+ (- b) (sqrt (- (quadrado b) (* 4 a c)))) (* 2 a))))

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

Aparentemente, tudo est bem. Contudo, h uma inecincia bvia: a funo raizes-equacao-segundo-grau calcula duas vezes o mesmo valor correspondente ao termo b2 4ac. Isso perfeitamente visvel na dupla ocorrncia da expresso (sqrt (- (quadrado b) (* 4 a c))).

112

Mais uma vez, atravs do uso de variveis locais e do operador setq que vamos simplicar e tornar mais eciente esta funo. Para isso, basta nos calcular o valor de b2 4ac, associando-o a uma varivel local e, em seguida, usamos esse valor nos dois locais em que necessrio. Do mesmo modo, podemos declarar uma varivel para conter o valor da expresso 2a e outra para conter b. O resultado ca ento:
(defun raizes-equacao-segundo-grau (a b c / raiz-b2-4ac 2a -b) (setq raiz-b2-4ac (sqrt (- (quadrado b) (* 4 a c))) 2a (* 2 a) -b (- b)) (cons (/ (- -b raiz-b2-4ac) 2a) (/ (+ -b raiz-b2-4ac) 2a)))

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

7.5

Variveis Globais

Qualquer referncia a um nome que no est declarado localmente implica que esse nome seja tratado como uma varivel global.37 O nome pi, por exemplo, representa a varivel pi e pode ser usado em qualquer ponto dos nossos programas. Por esse motivo, o nome pi designa uma varivel global.
Na realidade, o Auto Lisp permite ainda uma terceira categoria de nomes que no so nem locais, nem globais. Mais frente discutiremos esse caso.
37

113

A declarao de variveis globais mais simples que a das variveis locais: basta uma primeira atribuio, em qualquer ponto do programa, para a varivel car automaticamente declarada. Assim, se quisermos introduzir uma nova varivel global, por exemplo, para denir a razo de ouro38 1+ 5 = 1.6180339887 2 basta-nos escrever:
(setq razao-de-ouro (/ (+ 1 (sqrt 5)) 2))

A partir desse momento, o nome razao-de-ouro pode ser referenciado em qualquer ponto dos programas. importante referir que o uso de variveis globais deve ser restrito, na medida do possvel, denio 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 denem 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)) Tambm conhecida por proporo divina e nmero de ouro, entre outras designaes, e abreviada por em homenagem a Fineas, escultor Grego responsvel pela construo do Partnon onde, supostamente, usou esta proporo. A razo de ouro foi inicialmente introduzida por Euclides quando resolveu o problema de dividir um segmento de recta em duas partes de tal forma que a razo entre o segmento de recta e a parte maior fosse igual razo entre a parte maior e a menor. Se for a o comprimento do parte maior e b o da menor, b o problema de Euclides idntico a dizer que a+ = a . Daqui resulta que a2 ab b2 = 0 a b b b2 +4b2 ou que a = = b 12 5 . A raiz que faz sentido , obviamente, a = b 1+2 5 e, 2 = 1+2 5 . consequentemente, a razo de ouro = a b
38

114

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

7.6

Variveis Indenidas

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

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

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

115

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

7.7

Propores de Vitrvio

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

116

Uma vez que pretendiam erguer um templo com colunas mas no tinham conhecimento das propores adequadas, [. . . ] mediram o comprimento dum p de um homem e viram que era um sexto da sua altura e deram coluna uma proporo semelhante, i.e., zeram 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 gura masculina. Mais concretamente, Vitrvio caracteriza as colunas da Ordem Drica em termos do conceito de mdulo: A largura das colunas, na base, ser de dois modulos e a sua altura, incluindo os capitis, ser de catorze. Daqui se deduz um mdulo iguala o raio da base da coluna e que a altura da coluna dever ser 14 vezes esse raio. Dito de outra forma, o 1 raio da base da coluna dever ser 14 da altura da coluna. A altura do capitel ser de um mdulo e a sua largura de dois mdulos e um sexto. Isto implica que a altura do coxim somado do baco ser um mdulo, ou seja, igual ao raio da base da coluna e a largura do baco ser 1 de 2 6 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 1 3 do raio da base, e o coxim ter os restantes dois teros, 2 ou seja, 3 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 denio da funo ca ento:
(defun coluna (p r-base-fuste r-base-coxim

117

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


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

Usando esta funo j possvel desenhar colunas que se aproximam mais do padro drico (tal como estabelecido por Vitrvio).41 A Figura 26 apresenta as colunas desenhadas pelas seguintes invocaes:
(coluna (coluna (coluna (coluna (coluna (coluna
41

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

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

118

7.8

Seleco

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

O valor de uma combinao cujo primeiro elemento o if obtido da seguinte forma: 1. A expresso condicional avaliada. 2. Se o valor obtido da avaliao anterior verdade, o valor da combinao o valor da expresso consequente. 119

3. Caso contrrio, o valor obtido da avaliao anterior falso e o valor da combinao o valor da expresso alternativa. Este comportamento pode ser conrmado pelos seguintes exemplos:
_$ (if (> 3 2) 1 2) 1 _$ (if (> 3 4) 1 2) 2

Usando o if podemos denir 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 denirmos esta funo apenas precisamos de testar se o primeiro argumento maior que o segundo. Se for, a funo devolve o primeiro argumento, caso contrrio devolve o segundo. Com base neste raciocnio, podemos escrever:
(defun max (x y) (if (> x y) x y))

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

Um outro exemplo mais interessante ocorre com a funo matemtica sinal sgn, tambm conhecida como funo signum (sinal, em Latim). Esta funo pode ser vista como a funo dual da funo valor absoluto pois tem-se sempre x = sgn(x)|x|. A funo sinal denida por 1 se x < 0 sgn x = 0 se x = 0 1 caso contrrio 120

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

7.9

Seleco MltiplaA Forma cond

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

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

121

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

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

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

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

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

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

7.10

Seleco nas Propores de Vitrvio

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

123

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

O fragmento anterior corresponde, obviamente, armao: 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 simplicar a funo e escrever:
(defun raio-topo-fuste (raio-base altura) (cond ((< altura 15) (* (/ 5.0 6.0) raio-base)) ((< altura 20) (* (/ 5.5 6.5) raio-base)) ...))

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


(defun raio-topo-fuste (cond ((< altura 15) ((< altura 20) ((< altura 30) ((< altura 40) ((< altura 50) ...)) (raio-base altura) (* (/ 5.0 6.0) raio-base)) (* (/ 5.5 6.5) raio-base)) (* (/ 6.0 7.0) raio-base)) (* (/ 6.5 7.5) raio-base)) (* (/ 7.0 8.0) raio-base))

124

O problema agora que Vitrvio deixou a porta aberta para colunas arbitrariamente altas, dizendo simplesmente que no caso de colunas mais altas, determine-se proporcionalmente a diminuio com base nos mesmos princpios. Para percebermos claramente de que princpios estamos a falar, consideremos a evoluo da relao entre o topo e a base das colunas que visvel na imagem lateral. A razo entre o raio do topo da coluna e o raio da base da coluna , tal como se pode ver na margem (e j era possvel deduzir da funo raio-topo-fuste), uma sucesso da forma 6 61 7 5 51 , , , , 2 , 2 1 1 6 62 7 72 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 1 nmero de vezes que precisamos de somar 2 quer ao numerador quer ao 6 denominador de 7 . este tratar de forma diferente um caso e outro que, mais uma vez, sugere a necessidade de utilizao de uma estrutura de controle de seleco: necessrio distinguir dois casos e reagir em conformidade para cada um. No caso da coluna de Vitrvio, se a coluna tem uma altura a at 15 ps, a razo entre o topo e a base r = 5 6 ; se a altura a no inferior a 15 ps, a razo r entre o topo e a base ser: 6+ r= 7+
a20 10 a20 10

50
7 8

40
61 2 71 2

30
6 7

20 15

51 2 61 2

5 6

1 2 1 2

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

= 74 75 = 0.987. Este valor, por ser muito prximo da unidade, mostra que a coluna seria praticamente cilndrica. Com base nestas consideraes, podemos agora denir 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 simplicar a frmula para as colunas com altura no inferior a 15 ps. Assim, r= 6+ 7+
a20 10 a20 10

6+136/2 7+136/2

1 2 1 2

12 + 14 +

a20 10 a20 10

12 + 14 +

a 10 a 10

2 10 + = 12 + 2

a 10 a 10

A denio da funo ca 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 especicarmos 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 denio da funo:
(defun coluna-dorica (p altura / a-fuste r-base-fuste a-coxim r-base-coxim a-abaco l-abaco) (setq r-base-fuste (/ altura 14.0) r-base-coxim (raio-topo-fuste r-base-fuste altura) a-fuste (* 13.0 r-base-fuste) a-coxim (* (/ 2.0 3) r-base-fuste) a-abaco (* (/ 1.0 3) r-base-fuste) l-abaco (* (/ 13.0 6) r-base-fuste)) (fuste p a-fuste r-base-fuste r-base-coxim) (coxim (+z p a-fuste) a-coxim r-base-coxim (/ l-abaco 2.0)) (abaco (+z p (+ a-fuste a-coxim)) a-abaco l-abaco))

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

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

126

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


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

Exerccio 7.10.1 A funo raio-topo-fuste calcula o valor da varivel local divisoes mesmo quando o parmetro altura menor ou igual a 15. Redena a funo de modo a que a varivel divisoes s seja denida valor quando o seu valor realmente necessrio.

7.11

Operaes com Coordenadas

Vimos, na seco 4.1, a denio e implementao de um tipo abstracto para coordenadas bi-dimensionais que assentava nas seguinte funes: 127

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

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

O correspondente selector da coordenada z cou ento denido como:


(defun cz (c) (caddr c))

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

128

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

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

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

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

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

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

Como exemplo, temos:


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

Para alm da funo +xy tambm usmos anteriormente a funo +pol que somava a um ponto uma distncia especicada em termos de coordenadas polares:
(defun +pol (c ro fi) (+xy c (* ro (cos fi)) (* ro (sin fi))))

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

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

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

Agora, temos:
_$ (4 _$ (4 _$ (3 _$ (3 (+c (xy 1 2) 6) (+c (xy 1 2) 6 5) (+c (xyz 0 1 5 2) (+c (xyz 0 1 5 7) (xy 3 4)) (xyz 3 4 5)) 2) (xy 3 4)) 2) (xyz 3 4 5))

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

Agora, temos:
_$ (1 _$ (3 _$ (3 _$ (3 _$ (4 (-c (xy 4 6) 2) (-c (xy 4 6) 4)) (-c (xyz 4 6 4 0) (-c (xyz 4 6 4 5) (-c (xyz 5 7 5 6) (xy 3 4)) (xy 1 2)) 3) (xyz 1 2 3)) 5) (xy 1 2)) 9) (xyz 1 2 3))

131

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

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

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

7.12

Coordenadas Cilndricas

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

z P

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

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

No caso de pretendermos simplesmente somar a um ponto um vector em coordenadas cilndricas, podemos tirar partido da funo +c e denir:

133

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

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

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

134

P y

Figura 29: Coordenadas Esfricas

7.13

Coordenadas Esfricas

Tal como podemos ver na Figura 29, um ponto, em coordenadas esfricas (tambm denominadas polares), caracteriza-se pelo comprimento do raio vector, por um ngulo (denominado longitude ou azimute) que a projeco desse vector no plano z = 0 faz com o eixo x e por ngulo (denominado colatitude44 , znite ou ngulo polar) que o vector faz com o eixo z . Dado um ponto (, , ) em coordenadas esfricas, o mesmo ponto em coordenadas Cartesianas ( sin cos , sin sin , cos ) De igual modo, dado um ponto (x, y, z ) em coordenadas Cartesianas, o mesmo ponto em coordenadas esfricas ( y x2 + y 2 + z 2 , atan , atan x x2 + y 2 ) z

Tal como zemos para as coordenadas cilndricas, vamos denir o construtor de coordenadas esfricas e a soma de um ponto a um vector em coordenadas esfricas:
A colatitude , como bvio, o ngulo complementar latitude, i.e., a diferena entre o plo ( ) e a latitude. 2
44

135

(defun esf (ro fi (xyz (* ro (sin (* ro (sin (* ro (cos

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

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

7.14

Recurso

Vimos que as nossas funes, para fazerem algo til, precisam de invocar outras funes. Por exemplo, se j tivermos a funo que calcula o quadrado de um nmero e pretendermos denir 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 denir 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 denir 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 denir 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 zemos para a quarta-potencia, o cubo e o quadrado do-nos uma pista muito importante: se tivermos uma funo que calcula a potncia de expoente imediatamente inferior, ento basta-nos uma multiplicao adicional para calcular a potncia seguinte. Dito de outra forma, temos:
(defun potencia (x n) (* (potencia-inferior x n) x))

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

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

Apesar da nossa soluo engenhosa, esta denio tem um problema: qualquer que seja a potncia que tentemos calcular, nunca conseguiremos obter o resultado nal. 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 denio da funo potencia, ser preciso avaliar a expresso (* (potencia 4 2) 4) que, por sua vez, implica avaliar (* (* (potencia 4 1) 4) 4) que, por sua vez, implica avaliar (* (* (* (potencia 4 0) 4) 4) 4) que, por sua vez, implica avaliar (* (* (* (* (potencia 4 -1) 4) 4) 4) 4) que, por sua vez, implica avaliar (* (* (* (* (* (potencia 4 -2) 4) 4) 4) 4) 4) que, por sua vez, implica avaliar (* (* (* (* (* (* (potencia 4 -3) 4) 4) 4) 4) 4) 4) que, por sua vez, implica avaliar . . . fcil vermos que este processo nunca mais termina. O problema est no facto de termos reduzido o clculo da potncia de um nmero elevado a um expoente ao clculo da potncia desse nmero elevado ao expoente imediatamente inferior mas no dissemos em que situao que j temos um expoente sucientemente 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 sucientemente simples. No entanto, possvel ter um caso ainda mais simples: quando o expoente 1, o resultado simplesmente a base. Finalmente, o caso mais simples de todos: quando o expoente zero, o resultado 1, independentemente da base. Este ltimo caso fcil de perceber quando vemos que a avaliao de (potencia 4 2) (i.e., do quadrado de quatro) se reduz, em ltima anlise, a (* (* (potencia 4 0) 4) 4). 137

Para que esta expresso seja equivalente a (* 4 4) necessrio que (potencia 4 0) seja 1. Estamos ento em condies de denir 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 denida em termos de si prpria. Dito de outra forma, uma funo recursiva uma funo que se usa a si prpria na sua denio. Esse uso bvio quando desenrolamos a avaliao de (potencia 4 3): (potencia 4 3) (* (potencia 4 2) 4) (* (potencia 4 1) 4) 4) (* (potencia 4 0) 4) 4) 4) (* (* (* 1 4) 4) 4) (* (* 4 4) 4) (* 16 4) 64

(* (* (*

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

n! =

1, se n = 0 n (n 1)!, caso contrrio.

A traduo desta frmula para Lisp directa:


(defun factorial (n) (if (zerop n) 1 (* n (factorial (- n 1)))))

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

No usar correctamente o resultado da recurso para produzir o resultado originalmente pretendido. Repare-se que uma funo recursiva que funciona perfeitamente para os casos para que foi prevista pode estar completamente errada para outros casos. A funo factorial um exemplo: quando o argumento negativo, o problema torna-se cada vez mais complexo, cada vez mais longe do caso simples: (factorial -1) (* -1 (factorial -2)) (* -1 (* -2 (factorial -3))) (* -1 (* -2 (* -3 (factorial -4)))) (* -1 (* -2 (* -3 (* -4 (factorial -5)))))) (* -1 (* -2 (* -3 (* -4 (* -5 (factorial -6)))))) ... O caso mais frequente de erro numa funo recursiva a recurso nunca parar, ou porque no se detecta correctamente o caso bsico ou por a recurso no diminuir a complexidade do problema. Neste caso, o nmero de invocaes recursivas cresce indenidamente at esgotar a memria do computador, altura em que o programa gera um erro. No caso do Auto Lisp, esse erro no inteiramente bvio pois o avaliador apenas interrompe a avaliao sem apresentar qualquer resultado. Eis um exemplo:
_$ (factorial 3) 6 _$ (factorial -1) _$

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

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

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

141

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

r1 r0 p x

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

cuja avaliao dever gerar a imagem seguinte:

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

142

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

que devero gerar a imagem seguinte:

7.15

Depurao de Programas Recursivos

Vimos, na seco 4.19 que os erros de um programa se podem classicar 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 denio da funo potencia onde nos esquecemos da lista de parmetros: 143

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

Este esquecimento o suciente 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 denir, apresenta-nos um erro. Os erros semnticos so mais complexos que os sintticos pois, em geral, s podem ser detectados durante a execuo do programa.45 Por exemplo, se tentarmos calcular o factorial de uma string iremos ter um erro, tal como o seguinte exemplo mostra:
_$ (factorial 5) 120 _$ (factorial "cinco") ; error: bad argument type: numberp: "cinco"

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

Vimos, na seco4.19, que o Visual Lisp disponibiliza vrios mecanismos para fazermos a deteco de erros. Um dos mais simples a forma trace que permite visualizar as invocaes das funes. A forma trace recebe o
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.
45

144

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

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


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

Note-se, no texto anterior escrito em consequncia do trace, que a invocao que zemos 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 innita, 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:
Para se visualizar a janela de Trace basta seleccion-la a partir do menu Window do Auto Lisp.
46

145

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

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

146

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

Para se parar a depurao de uma funo, usa-se a forma especial untrace, que recebe o nome da funo ou funes que se pretende retirar do trace.
Exerccio 7.15.1 Faa o trace da funo potncia. Qual o trace resultante da avaliao (potencia 2 10)? Exerccio 7.15.2 Dena uma funo circulos capaz de criar a gura apresentada em seguida:

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

147

a P c Exerccio 7.15.4 Dena uma funo losangulos capaz de criar a gura apresentada em seguida:

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

7.16

Templos Dricos

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

148

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

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


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

cujo resultado est apresentado na Figura 30:


A denominada distncia interaxial, por oposio distncia intercolunar que mede o espao aberto entre colunas adjacentes.
47

149

Figura 30: Uma perspectiva de um conjunto de oito colunas dricas com 10 unidades de altura e 5 unidades de separao entre os eixos da colunas ao longo do eixo x.

150

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

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

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

Figura 31: Uma perspectiva do peristilo do templo de Segesta. As colunas foram geradas pela funo peristilo-dorico usando, como parmetros, 6 colunas na fronte e 14 no lado, com distncia intercolunar de 4.8 metros na fronte e 4.6 metros no lado e colunas de 9 metros de altura.

152

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

rp

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

154

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

Para o posicionamento das colunas, vamos tambm considerar um processo em que em cada passo apenas posicionamos uma coluna numa dada posio e, recursivamente, colocamos as restantes colunas a partir da posio circular seguinte. Dada a sua estrutura circular, a construo deste gnero de edifcios simplicada pelo uso de coordenadas circulares. De facto, podemos conceber um processo recursivo que, a partir do raio rp do peristilo e do ngulo inicial , coloca uma coluna nessa posio e que, de seguida, coloca as restantes colunas usando o mesmo raio mas incrementando o ngulo de , tal como se apresenta na Figura 34. O incremento angular obtmse pela diviso da circunferncia pelo nmero n de colunas a colocar, i.e., = 2n . Uma vez que as colunas se dispem em torno de um crculo, o clculo das coordenadas de cada coluna ca facilitado pelo uso de coordenadas polares. Tendo este algoritmo em mente, a denio da funo ca:
(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, denimos a funo tholos que, dados os parmetros necessrios s duas anteriores, as invoca em sequncia: 155

ap ab rb rp rp y

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

156

Figura 35: Perspectiva do Tholos de Atenas em Delfos, constitudo por 20 colunas de estilo Drico, de 4 metros de altura e colocadas num crculo com 7 metros de raio.
(defun tholos (p n-degraus rb dab drb n-colunas rp ap) (base-tholos p n-degraus rb dab drb) (colunas-tholos (+xyz p 0 0 (* n-degraus dab)) n-colunas rp 0 (/ 2*pi n-colunas) ap))

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


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

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

157

Dena 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 inuencia o baco, pelo que dever tambm denir uma funo para desenhar um baco rodado. De seguida, redena a funo colunas-tholos de modo a que cada coluna esteja orientada correctamente relativamente ao centro do Tholos. Exerccio 7.16.2 Considere a construo de uma torre composta por vrios mdulos em que cada mdulo tem exactamente as mesmas caractersticas de um Tholos, tal como se apresenta na gura abaixo, esquerda:

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

158

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

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

159

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

7.17

A Ordem Jnica

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

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

161

rf

P v1 v rf f f 2 P2 P3

P1 v0

rf f

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

162

Como se v pela gura, 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 coeciente de reduo da 3 espiral. Este segundo arco vai desde o ngulo at ao ngulo 2 . 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 nal 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 car 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 especicar 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 nal + 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 denir 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 denir 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 nal a-fin e o factor de reduo f:
(defun espiral (p r a-ini a-fin f) (if (> a-ini a-fin) nil (progn (quarto-circunferencia p r a-ini) (espiral (+pol p (* r (- 1 f)) (+ a-ini (/ pi 2))) (* r f)

163

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

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

Podemos agora experimentar um exemplo:


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

A espiral traada pela expresso anterior est representada na Figura 38. A funo espiral permite-nos denir um sem-nmero de espirais, mas tem uma restrio: cada arco de crculo corresponde a um ngulo de 2 . Logicamente, a funo tornar-se- mais til se tambm este incremento de ngulo for um parmetro. As modicaes 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 denio , ento:
(defun espiral (p r a-ini a-inc a-fin f) (if (> a-ini a-fin) nil

164

Figura 38: O desenho da espiral.


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

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

Agora, para desenhar a mesma espiral representada na Figura 38, temos de avaliar a expresso: 165

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

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

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

Os resultados esto representados na Figura 40.


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

166

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

Um vulo composto por quatro arcos de circunferncia concordantes, tal como se apresenta na imagem seguinte:

167

r1

r2

r2

r0

Os arcos de crculos necessrios para a construo do ovo so caracterizados pelos raios r0 , r1 e r2 . Note que o arco de circunferncia de raio r0 cobre um ngulo de e o arco de circunferncia de raio r2 cobre um ngulo de . Dena uma funo ovo que desenha um ovo. A funo dever receber, apenas, as coordenadas do ponto P , os raios r0 e r1 e, nalmente, a altura h do ovo.

7.18

Recurso na Natureza

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

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

169

f c

f c c

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

170

Figura 43: Uma rvore de comprimento 20, ngulo inicial 2 , abertura e factor de reduo 0.7.
(arvore topo (* comprimento f) (+ angulo alfa) alfa f) (arvore topo (* comprimento f) (- angulo alfa) alfa f)))) (defun ramo (base topo) (command "_.line" base topo "")) (defun folha (topo) (command "_.circle" topo 0.2))

Um primeiro exemplo de rvore gerado com a expresso


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

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

171

Figura 44: Vrias rvores geradas com diferentes ngulos de abertura e factores de reduo do comprimento dos ramos.

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

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

172

e fe c

d fd c c

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

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

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

173

Figura 46: Vrias rvores geradas com diferentes ngulos de abertura e factores de reduo do comprimento para os ramos esquerdo e direito.

174

Funo + -

Argumentos Vrios nmeros Vrios nmeros

* / 1+ 1abs sin cos atan

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

sqrt exp expt log max min rem

Um nmero no negativo Um nmero Dois nmeros Um nmero positivo Vrios nmeros Vrios nmeros Dois ou mais nmeros

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

fix float gcd

Um nmero Um nmero Dois nmeros

Tabela 2: Funes matemticas pr-denidas do Auto Lisp. 175

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

xn )

xn )

xn )

xn )

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

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

176

Funo strcat strlen

Argumentos Vrias strings Vrias strings

substr

strcase

Uma string, um inteiro (o ndice) e, opcionalmente, outro inteiro (o nmero de caracteres) Uma string e um boleano opcional Uma string Uma string 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.

atof atoi itoa rtos

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 especicado no segundo argumento e com a preciso especicada no terceiro argumento.

Tabela 4: Operaes pr-denidas envolvendo strings.

177

Você também pode gostar