0 évaluation0% ont trouvé ce document utile (0 vote)
2K vues170 pages
O documento apresenta uma introdução à programação funcional, abordando conceitos básicos da programação e da linguagem Haskell. É dividido em 19 capítulos que discutem tópicos como conceitos introdutórios, resolução de problemas, tipos de dados, listas, paradigma funcional entre outros.
O documento apresenta uma introdução à programação funcional, abordando conceitos básicos da programação e da linguagem Haskell. É dividido em 19 capítulos que discutem tópicos como conceitos introdutórios, resolução de problemas, tipos de dados, listas, paradigma funcional entre outros.
O documento apresenta uma introdução à programação funcional, abordando conceitos básicos da programação e da linguagem Haskell. É dividido em 19 capítulos que discutem tópicos como conceitos introdutórios, resolução de problemas, tipos de dados, listas, paradigma funcional entre outros.
Credin SiIva de Menezes, Maria CIaudia SiIva Boeres, Maria Christina VaIIe Rauber, Thais HeIena Castro, AIberto Nogueira de Castro Jnior, CIudia GaIarda Varassin Departamento de Informtica - UFES Departamento de Cincia da Computao - UFAM 2008 1 ndice 1. CONCETOS BSCOS ................................................................................ 4 2. A LNGUAGEM DE PROGRAMAO HASKELL E O AMBENTE HUGS 12 3. A ARTE DE RESOLVER PROBLEMAS ..................................................... 21 4. ABSTRAO, GENERALZAO, NSTANCAO E MODULARZAO ................................................................................................................................. 28 5. TPOS DE DADOS NUMRCOS ............................................................... 36 6. EXPRESSES LGCAS E O TPO BOOLEAN ....................................... 51 7. DEFNES CONDCONAS ................................................................... 59 8. O TESTE DE PROGRAMAS ...................................................................... 66 9. RESOLVENDO PROBLEMAS - OS MOVMENTOS DO CAVALO ........... 72 10. TUPLAS ..................................................................................................... 81 11. VALDAO DE DADOS .......................................................................... 86 12. LSTAS ...................................................................................................... 91 13. RESOLVENDO PROBLEMAS COM LSTAS ......................................... 105 14. PARADGMA APLCATVO .................................................................... 109 15. Processamento de Cadeias de Caracteres primeiros passos ............. 120 16. O PARADGMA RECURSVO ................................................................ 127 17. ORDENAO RECURSVA DE DADOS...............................................142 18. APLCAES ......................................................................................... 152 19. ENTRADA E SADA DE DADOS ............................................................ 168 2 1. CONCEITOS BSICOS 1.1 INTRODUO Neste curso o leitor estar se envolvendo com a aprendizagem de conceitos e mtodos bsicos para a construo de programas de computador. A abordagem que daremos est voltada para o envolvimento do aprendiz com a soluo de problemas ao invs da atitude passiva de ver o que os outros fizeram. Uma questo central que permeia o curso a de que construir programas uma tarefa de engenharia, e que, portanto produzir artefatos com os quais o ser humano ter de conviver. Artefatos estes que devem satisfazer requisitos de qualidade e serem, portanto, passveis de constatao. Optamos desenvolver a disciplina orientada descrio de funes, um formalismo bastante conhecido por todos os que chegam a este curso. Esperamos, com isso, atenuar algumas dificuldades tpicas do ensino introdutrio de programao. Nas sees seguintes apresentamos alguns conceitos bsicos que nos parecem importantes ter em mente antes de iniciarmos um curso de programao. 1.2. COMPUTADORES Denominamos computador uma mquina de processar dados, numricos ou simblicos, que funciona atravs da execuo de programas. Ao contrrio das inmeras mquinas que conhecemos, tais como mquina de lavar roupa, liquidificador, aspirador de p, e tantas outras, que realizam uma nica funo, o computador uma mquina multi-uso. Podemos us-lo como uma mquina de escrever sofisticada, como uma mquina de fax, como uma prancheta de desenho sofisticada, como um fichrio eletrnico, como uma planilha de clculos e de tantas outras formas. exatamente como o nosso conhecido videogame: para mudar de jogo basta trocar o cartucho. No videogame, cada novo jogo determinado por um novo programa. Em linhas gerais podemos entender um computador como uma mquina capaz de: a) interpretar dados que lhe so fornecidos, produzindo resultados em forma de novos dados, usando para isso conceitos que lhe foram antecipadamente informados e, b) aceitar a descrio de novos conceitos e consider-los na interpretao de novas situaes. Alguns exemplos de uso de um computador: 1) Descrever para uma mquina a relao mtrica que existe entre os lados de um tringulo retngulo. De posse desse conhecimento, a mquina poderia, por exemplo, determinar o valor de um dos lados quando conhecido o valor dos outros dois. 3 2) nformar a uma mquina as regras de conjugao de verbos. Com este conhecimento a mquina pode determinar a forma correta para um determinado tempo e pessoa de um verbo especfico. 3) Traduo de textos; 4) Classificao de textos quanto sua natureza: romance, poesia, documentrio, entrevista, artigo cientfico; 5) Manipulao de expresses algbricas, resoluo de integral indefinida, etc; 6) Programao automtica: dada uma certa especificao, gerar um programa eficiente; 7) Monitoramento de pacientes em um Centro de Tratamento ntensivo; 8) dentificao de tumores no crebro a partir da comparao de imagens com padres conhecidos de anormalidade; 9) Roteamento inteligente de mensagens; 10) Monitoramento de regies por satlite. 1.3. PROGRAMAO tarefa de identificar o conhecimento necessrio para a descrio de um conceito, organiz-lo e codific-lo de modo a ser entendido pela mquina damos o nome de programao de computadores. Ao conhecimento codificado, produto final da tarefa de programao d-se o nome de programa. A programao de computadores uma atividade que compreende vrias outras atividades, tais como: entendimento do problema a ser resolvido, planejamento de uma soluo, formalizao da soluo usando uma linguagem de programao, verificao da conformidade da soluo obtida com o problema proposto. 1.4. LINGUAGEM DE PROGRAMAO A descrio de conhecimento para um agente racional qualquer (seja uma mquina ou um humano) subentende a existncia de padres segundo os quais o agente possa interpretar o conhecimento informado. A esses padres, quando rigorosamente elaborados, damos o nome de formalismo. Um formalismo composto de dois aspectos: a sintaxe e a semntica. A sintaxe permite ao agente reconhecer quando uma "seqncia de smbolos" que lhe fornecida est de acordo com as regras de escrita e, portanto representa um programa. A semntica permite que o agente atribua um significado ao conhecimento descrito pela "seqncia de smbolos". Por exemplo, quando um agente humano (com determinado grau de escolaridade) encontra a seguinte seqncia de smbolos {3, 4} U {5, 9, 15}, ele por certo reconhecer como uma expresso algbrica escrita corretamente e, se lembrar dos fundamentos da 4 teoria dos conjuntos, associar esta cadeia como a descrio de um conjunto composto pela unio dos elementos de dois conjuntos menores. Eis aqui algumas observaes importantes sobre a necessidade de linguagens de programao: Ainda no possvel usar linguagem natural para ensinar o computador a realizar uma determinada tarefa. A linguagem natural, to simples para os humanos, possui ambigidades e redundncias que a inviabilizam como veculo de comunicao com os computadores. A linguagem nativa dos computadores muito difcil de ser usada, pois requer do programador a preocupao com muitos detalhes especficos da mquina, tirando pois ateno do problema. Para facilitar a tarefa de programao foram inventadas as linguagens de programao. Estas linguagens tm por objetivo se colocarem mais prximas do linguajar dos problemas do que do computador em si. Para que o programa que escrevemos possa ser "entendido pelo computador, existem programas especiais que os traduzem (compiladores) ou os que interpretam (interpretadores) para a linguagem do computador. Podemos fazer um paralelo com o que ocorre quando queremos nos comunicar com uma pessoa de lngua estrangeira. Podemos escrever uma carta em nossa lngua e pedir a algum que a traduza para a lngua de nosso destinatrio ou se quisermos conversar pessoalmente, podemos usar um intrprete. 1.5. PROPRIEDADES DE UM PROGRAMA Fazemos programas com a inteno de dotar uma mquina da capacidade de resolver problemas. Neste sentido, um programa um produto bem definido, que para ser usado precisa que sejam garantidas algumas propriedades. Aqui fazemos referncias a duas delas: a correo e o desempenho. A correo pode ser entendida como a propriedade que assegura que o programa descreve corretamente o conhecimento que tnhamos inteno de descrever. O desempenho trata da propriedade que assegura que o programa usar de forma apropriada o tempo e os recursos da mquina considerada. Cabe aqui alertar aos principiantes que a tarefa de garantir que um programa foi desenvolvido corretamente to complexa quanto prpria construo do programa em si. Garantir que um programa funciona corretamente condio imprescindvel para o seu uso e, portanto estaremos dando maior nfase a esta propriedade. 1.6. PARADIGMAS DE LINGUAGENS DE PROGRAMAO As regras que permitem a associao de significados s "seqncias de smbolos" obedecem a certos princpios. Existem vrias manifestaes destes princpios e a cada uma delas denominamos de paradigma. Um paradigma pode ser entendido informalmente como uma forma especfica de se "pensar" sobre programao. Existem trs grandes grupos de 5 paradigmas para programao: o procedimental (ou procedural), o funcional e o lgico. Os dois ltimos so freqentemente referidos como sendo subparadigmas de um outro mais geral, o paradigma declarativo. O paradigma procedimental subentende a organizao do conhecimento como uma seqncia de tarefas para uma mquina especfica. O paradigma lgico requer o conhecimento de um formalismo matemtico denominado lgica matemtica. O paradigma funcional baseia-se no uso dos princpios das funes matemticas. De uma forma geral, os paradigmas declarativos enfatizam o aspecto correo e o procedimental os aspectos de desempenho. Vejam que falamos em "enfatizam", o que quer dizer que apresentam facilidades para descrio e verificao da propriedade considerada. Entretanto, em qualquer caso, o programador dever sempre garantir que os dois aspectos (correo e desempenho) sejam atendidos. 1.7. PROGRAMAO FUNCIONAL Para os fins que aqui nos interessam neste primeiro momento, podemos entender o computador, de uma forma simplificada, como uma mquina capaz de: a) avaliar expresses escritas segundo regras sintticas bem definidas, como a das expresses aritmticas que to bem conhecemos (ex. 3 + 5 - 8) obedecendo semntica das funes primitivas das quais ela dotada (por exemplo: as funes aritmticas bsicas como somar, subtrair, multiplicar e dividir); b) aceitar a definio de novas funes e posteriormente consider-las na avaliao de expresses submetidas sua avaliao. Por enquanto, denominaremos o computador de mquina funcional. Na Figura 1.1 apresentamos um exemplo de interao de um usurio com a nossa Mquina Funcional. usurio: 3 + 5 / 2 Mquina funcional:: 5,5 usurio: f x y = (x + y) / 2 Mquina funcional: definio de f foi aceita usurio: (f 3 5) + (f 10 40) Mquina funcional: 29 Figura 1.1 Na primeira interao podemos observar que o usurio descreveu uma expresso aritmtica e que a mquina funcional avaliou e informou o resultado. Na segunda interao o usurio descreve, atravs de uma equao, uma nova funo, que ele denominou de f e que a mquina funcional acatou a nova definio. Na terceira interao o usurio solicita a avaliao de uma nova expresso aritmtica usando o conceito recentemente definido e que a mquina funcional faz a avaliao usando corretamente o novo conceito. Desta forma, percebemos que a mquina funcional capaz de avaliar expresses 6 aritmticas e funes e tambm aceitar definies de funes, usando para isso, ambientes distintos. 1.8. EXPRESSES ARITMTICAS A nossa mquina funcional hipottica entende a sintaxe das expresses aritmticas, com as quais todo aluno universitrio j bem familiarizado e capaz de avali-las usando as mesmas que regras que j conhecemos. Sintaxe - Todo operador aritmtico pode ser entendido, e aqui o ser, como uma funo que possui dois parmetros. A notao usual para as operaes aritmticas a infixada, ou seja, smbolo funcional colocado entre os dois operandos. Nada impede de pensarmos nelas escritas na forma prefixada, que a notao usual para funes com nmero de parmetros diferente de 2. Por exemplo, podemos escrever "+ 3 2" para descrever a soma do nmero 3 com o nmero 2. As funes definidas pelo programador devem ser escritas de forma prefixada, como no exemplo de interao apresentado na Figura 1.1. Combinando essas duas formas, infixada e prefixada, podemos escrever expresses bastante sofisticadas. AvaIiao - As expresses aritmticas, como sabemos, so avaliadas de acordo com regras de avaliao bem definidas, efetuando as operaes de acordo com suas prioridades. Por exemplo, na expresso "3 + 5 / 2" o primeiro operador a ser avaliado ser o de diviso (/) e posteriormente o de adio. Se desejarmos mudar essa ordem, podemos usar parnteses em qualquer quantidade, desde que balanceados e em posies apropriadas. Por exemplo, na expresso "(3 + 5) / 2", a utilizao de parnteses determina que a sub- expresso 3 + 5 ter prioridade na avaliao. 1.9. FUNES Podemos entender o conceito de funes como uma associao entre elementos de dois conjuntos A e B de tal forma que para cada elemento de A existe apenas um elemento de B associado. O conjunto A conhecido como o domnio da funo, ou ainda como o conjunto de entrada, e o conjunto B o contra-domnio ou conjunto de sada. Para ser mais preciso, podemos afirmar que uma funo f , que associa os elementos de um conjunto A aos elementos de um conjunto B, consiste em um conjunto de pares ordenados onde o primeiro elemento do par pertence a A o segundo a B. Exemplos:
a) Seja a funo T que associa as vogais do alfabeto com os cinco primeiros inteiros positivos. T = {(a,1), (e,2), (i,3), (o,4), (u,5)} b) Seja a funo Q, que associa a cada nmero natural o seu quadrado. Q = {(0,0), (1,1), (2,4), (3,9), (4,16), ...} Podemos observar que a funo T um conjunto finito e que a funo Q um conjunto infinito. 7 1.10. DESCRIES FUNCIONAIS Podemos descrever um conjunto, de duas formas: extensional, onde explicitamos todos os elementos que so membros do conjunto, como no caso do conjunto T apresentado anteriormente; ou na forma intencional, onde descrevemos um critrio de pertinncia dos membros do conjunto. Por exemplo, o conjunto Q acima apresentado poderia ser reescrito da seguinte forma: Q = {(x, y) | x naturaI e y = x.x} que pode ser lido da seguinte maneira: Q o conjunto dos pares ordenados (x, ) tal que x u! n"!ero natural e o produto de x por x# Quando descrevemos uma funo para fins computacionais, estamos interessados em explicitar como determinar o segundo elemento do par ordenado, conhecido o primeiro elemento do par. Em outras palavras, como determinar y conhecendo-se o valor de x. Normalmente dizemos que queremos determinar y em funo de x. Nesta forma de descrio, omitimos a varivel y e explicitamos o primeiro elemento que denominado ento de parmetro da funo. No caso acima teramos ento: Q x = x . x 1.11. POR QUE COMEAR A APRENDIZAGEM DE PROGRAMAO ATRAVS DO PARADIGMA FUNCIONAL? Tendo em vista a prtica vigente de comear o ensino de programao em cursos de computao utilizando o paradigma procedimental, apresentamos a seguir alguns elementos que baseiam nossa opo de comear o ensino de programao usando o paradigma funcional. 1) O aluno de graduao em Computao tem de 4 a 5 anos para aprender todos os detalhes da rea de computao, portanto no se justifica que tudo tenha que ser absorvido no primeiro semestre. O curso introdutrio apenas o primeiro passo e no visa formar completamente um programador. Este o momento de apresentar-lhe os fundamentos e, alm disso, permitir que ele vislumbre a variedade de problemas que podem ser solucionados como o apoio do computador; 2) O paradigma procedimental requer que o aluno tenha um bom entendimento dos princpios de funcionamento de um computador real, pois eles se baseiam, como as mquinas reais, no conceito de mudana de estados (mquina de Von Neumann). 3) O paradigma lgico, outro forte candidato, requer o conhecimento de lgica matemtica que o aluno ainda no domina adequadamente; 4) O paradigma funcional baseado num conhecimento que o aluno j est familiarizado desde o ensino mdio (funes, mapeamento entre domnios) o qual ainda explorado em outras disciplinas do 8 ciclo bsico, o que nos permite concentrar nossa ateno na elaborao de solues e na descrio formal destas; 5) O elevado poder de expresso das linguagens funcionais permite que a abrangncia do uso do computador seja percebida mais rapidamente. Em outras palavras, podemos resolver problemas mais complexos j no primeiro curso; 6) O poder computacional do paradigma funcional idntico ao dos outros paradigmas. Apesar disso, ele ainda no usado nas empresas, por vrios aspectos. Dentre os quais podemos citar: i) No passado, programas escritos em linguagens funcionais eram executados muito lentamente. Apesar disso no ser mais verdadeiro, ficou a fama; ii) A cultura de linguagens procedimentais possui muitos adeptos no mundo inteiro, o que, inevitavelmente, cria uma barreira introduo de um novo paradigma. Afinal, temos medo do desconhecido e trememos quando temos que nos livrar de algo que j sabemos; iii) H uma crena que linguagens funcionais so difceis de aprender e s servem para construir programas de inteligncia artificial. 7) A ineficincia das linguagens funcionais em comparao s procedimentais tem se reduzido atravs de alguns mecanismos tais como: lazy evaluation, grafo de reduo, combinadores. 8) Para fazer um programa que "funciona" (faz alguma coisa, embora no necessariamente o que desejamos) mais fcil faz- lo no paradigma procedimental. Para fazer um programa que funciona "corretamente" para resolver um determinado problema mais fcil no paradigma funcional, pois esse paradigma descreve "o que fazer" e no "como fazer". 9) As linguagens funcionais so geralmente utilizadas para processamento simblico, ou seja, soluo de problemas no numricos. (Exemplo: integrao simblica X integrao numrica). Atualmente constata-se um crescimento acentuado do uso deste tipo de processamento. 10) A crescente difuso do uso do computador nas mais diversas reas do conhecimento gera um crescimento na demanda de produo de programas cada vez mais complexos. Os defensores da programao funcional acreditam que o uso deste paradigma seja uma boa resposta a este problema, visto que com linguagens funcionais podemos nos concentrar mais na soluo do problema do que nos detalhes de um computador especfico, o que aumenta a produtividade. 11) Se mais nada justificar o aprendizado de uma linguagem funcional como primeira linguagem, resta a explicao didtica. Estamos dando um primeiro passo no entendimento de programao como um todo. , portanto, importante que este 9 passo seja simplificado atravs do apoio em uma "mquina" j conhecida, como o caso das funes. 12) Ainda um outro argumento: mesmo que tenhamos que usar posteriormente uma outra linguagem para ter uma implementao eficiente, podemos usar o paradigma funcional para formalizar a soluo. Sendo assim, a verso em linguagem funcional poderia servir como uma especificao do programa. Exerccios 1. Conceitue programao de computadores. 2. Quais os principais paradigmas de programao e o que os diferenciam. 3. Faa uma descrio intencional da funo: F = {1,3,5,7,...}. 4, Faa uma listagem de outros exemplos de programas de computador que so usados hoje em diferentes reas do conhecimento e por diferentes profissionais. 5. Apresente exemplo de outras linguagens tcnicas usadas pelo ser humano para descrever conhecimento. 6. Os conceitos de correo e de desempenho, se aplicam a qualquer artefato. Escolha 3 artefatos quaisquer e discuta os dois conceitos. 7. Apresente uma descrio informal da funo que conjuga os verbos regulares da 1 a . conjugao. 10 2. A LINGUAGEM DE PROGRAMAO HASKELL E O AMBIENTE HUGS 2.1. INTRODUO Neste curso usaremos o ambiente HUGS, no qual utilizada uma implementao da linguagem de programao funcional Haskell. Essa linguagem por apresentar uma sintaxe simples e elegante, alm de oferecer operadores bastante expressivos, tem sido usada com bons resultados para a aprendizagem de fundamentos de programao. Podemos usar o HUGS como uma calculadora qualquer, qual submetemos expresses que ela avalia e nos informa o valor resultante. Vejamos por exemplo as interaes a seguir. ? 3 + 5 * 2 13 ? (3 + 5) * 2 16 ? Nas expresses acima importante destacar o uso do smbolo * (asterisco) que empregado para representar a multiplicao. Alm desse, outros smbolos usuais sero substitudos, como veremos logo mais. As operaes aritmticas so, como sabemos, funes. A notao utilizada em Haskell para as operaes aritmticas a usual, ou seja, infixada (o smbolo da operao fica entre os operandos). Uma outra forma existente em Haskell para escrever expresses usando o operador de forma prefixada. Por exemplo,a expresso di$ %5 & , indica a diviso inteira do nmero 15 (dividendo) pelo nmero 6 (divisor). No ambiente HUGS, o smbolo ' usado para indicar que o sistema est preparado para avaliar uma nova expresso. Aps avaliar uma expresso o resultado informado na linha seguinte e em seguida uma nova interrogao exibida, se disponibilizando para uma nova avaliao. O avaliador de expresses do ambiente HUGS funciona conforme o esquema da figura 2.1. Repetidamente o ambiente HUGS permite a leitura de uma expresso, sua avaliao e exibio do resultado. Figura 2.1 Podemos usar o ambiente HUGS somente para isso, para escrever expresses e solicitar ao sistema que as avalie. Entretanto podemos ousar mais e usar o ambiente para descrever novas funes a partir das funes j oferecidas pelo ambiente. As funes j oferecidas pelo ambiente so conhecidas como primitivas. Podemos tambm utilizar nessas novas definies de funes aquelas que tivermos construdo anteriomente. 2.2. DESCRIO DE FUNES A forma de descrever funes similar ao que nos referimos anteriormente no Captulo 1, ou seja, atravs de uma equao, onde no lado esquerdo da igualdade definimos um nome para a funo e relacionamos os parmetros (ou argumentos) considerados na sua definio. No lado direito escrevemos uma expresso utilizando outras funes, primitivas ou no. sto nos leva portanto a tomar conhecimento que a linguagem possui funes primitivas que j a acompanham e que portanto prescindem de definio. Por exemplo, para definir a funo que determina o espao percorrido por um mvel em movimento retilneo uniforme, conhecidos a sua velocidade e o tempo decorrido, podemos escrever: espaco $ t ( $ ) t No esquema a seguir fazemos uma identificao didtica dos vrios elementos da definio. O lado esquerdo da igualdade tambm chamado de inteface ou assinatura da funo e o lado direito o corpo da definio. espaco v t = v * t nome da funo parmetros expresso aritmtica que define a relao que h entre os parmetros interface da funo corpo da definio Para alimentar o HUGS com novas definies devemos criar um arquivo texto em disco no qual editaremos as definies desejadas. Cada arquivo pode ter uma ou mais definies de funes. Normalmente agrupamos em um mesmo arquivo as definies relacionadas com a soluo de um problema especfico ou definies de propsito geral. No jargo de programao com a linguagem Haskell, um conjunto de definies denominado script. A alimentao de novas definies indicada ao sistema atravs de um comando que usado no lugar de uma expresso, quando o sistema exibe o seu pedido de tarefa (o smbolo de interrogao). Para indicar que estaremos executando um comando, usamos o smbolo " : " (dois pontos) seguido do nome do comando (existem vrios). Para a tarefa que temos neste instante utilizamos o comando load (oportunamente veremos outros). Por exemplo, podemos escrever um conjunto de definies em um arquivo denominado pf001.hs. Para alimentar este arquivo no ambiente HUGS (interpretador) podemos escrever: > :Ioad pf001.hs A partir deste momento, todas as definies contidas no arquivo informado estaro disponveis para uso. sso pode ser entendido como uma extenso da mquina Haskell. Na Figura 2.2 apresentamos um esquema de utilizao do ambiente HUGS e de um editor de textos para provimento de novas definies. 12 Figura 2.2 nteraes do Programador com o Ambiente de Programao Vale aqui um pequeno lembrete, podemos estender a noo de funes para incorporar tambm as chamadas funes constantes. Na nomenclatura de definies de funes, dizemos que temos definies paramtricas e definies no paramtricas. Por exemplo, podemos definir a constante pi, da seguinte maneira: pi = 3.1416 A constante pi pode ser entendida como uma funo no paramtrica. 2.3. UM EXEMPLO Considere que queremos descrever uma funo para determinar as razes de uma equao do segundo grau.Sabemos que pela nossa clssica frmula as razes so descritas genericamente por: A soluo, como sabemos, formada por um par de valores. Por enquanto vamos descrever este fato por duas funes, uma para a primeira raiz e outra para a segunda. eq2g1 a b c = ((-b) + sqrt (b^2 - 4! " a " c )) # (2! " a) Vamos discutir alguns detalhes desta codificao: 13 Editor de texto Arquivo texto com definies de funes Ambiente HUGS (interpretador) Programador o termo *+ precisa ser codificado entre parntesis pois nmeros negativos so obtidos por um operao unria que produz um nmero negativo a partir de um positivo; o smbolo da raiz quadrada foi substitudo pela funo sqrt; o numerador da frao precisa ser delimitado pelo uso de parntesis; o denominador tambm precisa ser delimitado por parntesis; o smbolo de multiplicao usual foi trocado pelo * (asterisco); o smbolo de potenciao foi substitudo pelo ^ (circunflexo). Podemos agora descrever a outra raiz de forma anloga: eq2g2 a b c = ((-b) - sqrt (b^2 - 4! " a " c )) # (2! " a) Visto que as duas descries possuem partes comuns, poderamos ter escrito abstraes auxiliares e produzir um conjunto de definies, tal como: quad x = x " x raizdelta a b c = sqrt ( quad b - 4! " a " c ) dobro x = 2! " x eq2g1 a b c = ((-b) + rai$delta a b c) # dobro a eq2g2 a b c = ((-b) - rai$delta a b c) # dobro a
Vejamos como ficaria uma interao com o HUGS, a partir de um arquivo de definies denominado eq2g.hs: % :l eq2g&s 'eading script file (eq2g&s() *as+ell session for) standardprelude eq2g&s > eq2g1 2.0 5.0 2.0 -0.5 > eq2g2 2.0 5.0 2.0 -2.0 > eq2g1 3.0 4.0 5.0 ,rogram error) -sqrt (-44!). Podemos observar que houve um problema com a avaliao da expresso eq2,% 3#- .#- 5#- /ste u! erro se!0ntico, pro$ocado pela tentati$a de extrair a rai1 quadrada de u! n"!ero ne,ati$o# 2 fun34o que defini!os portanto u!a fun34o parcial, ou 14 seja, ela n4o est definida para todo o do!5nio dos reais# 6este caso o siste!a apenas acusa o pro+le!a ocorrido# 2.4. DEFINIES LOCAIS As definies que discutimos anteriormente so globais, no sentido de que esto acessveis ao uso direto do usurio e tambm disponveis para uso em qualquer outra definio. Por exemplo, se temos o script quad x = x * x hipo x y = sqrt ( quad x + quad y) podemos utilizar a definio quad tanto externamente pelo usurio quanto internamente pela definio 7ipo. Se no entanto desejamos construir subdefinies para uso em uma definio especfica, podemos defin-las internamente, restringindo o seu contexto. A maneira de introduzir definies locais em Haskell por meio da clusula 87ere# Vamos modificar o script acima para que quad s possa ser usado no interior de 7ipo. hipo x y = sqrt ( quad x + quad y) where quad x = x * x
As definies internas tambm no precisam ser paramtricas. Veja este outro exemplo. hipo x y = sqrt ( k1 + k2) where
k1 = x * x k2 = y * y
Note que apesar de x e no serem parmetros de 9% e 92 eles foram utilizados em suas definies. sto possvel porque x e tm validade em todo o lado direito da definio. Temos que considerar ainda que nada impede que em uma definio local tenhamos uma outra definio local e dentro desta outra, e assim sucessivamente. hipo x y = sqrt k where k = quad x + quad y where quad x = x * x
2.5. MODELO DE AVALIAO DE EXPRESSES: Quando o avaliador (interpretador) do HUGS toma uma expresso contendo apenas constantes e operaes primitivas, ele apenas efetua as operaes obedecendo prioridade dos operadores e aquelas determinadas pelo uso de parntesis. Por exemplo, para avaliar a expresso 3 + 5 / 2, 15 primeiro realizada a diviso 5/2, resultando em 2.5, o qual adicionado ao 3, finalmente obtendo 5.5. Podemos entender isso como uma seqncia de redues de uma expresso outra mais simples, at que se atinja um termo irredutvel. Veja os exemplos a seguir: 3 + 5 / 2 3 + 2.5 5.5 (3 + 5) / 2 8 / 2 4 3 + 5 + 2 8 + 2 10 As setas () so usadas para indicar um passo de reduo. Quando as expresses utilizam funes definidas pelo usurio, o processo anlogo. Cada referncia a uma definio substituda por seu lado direito, at que se atinja uma expresso bsica, e prossegue como no caso anterior. Vejamos os exemplos abaixo, considerando a primeira definio de 7ipo e de quad apresentada anteriormente:
Para uma realizao especfica da linguagem, isso no precisa acontecer exatamente assim. Entretanto este modelo suficiente para nossos interesses. O nmero de redues necessrias para chegar forma irredutvel de uma expresso, tambm denominada de forma cannica ou ainda forma normal, pode ser usado como critrio para discutir o desempenho da mesma. Exerccios 16 1. Estabelea categorias para comparar duas definies; 2. Usando as categorias definidas no item 1 compare as duas definies apresentadas para as razes de uma equao do segundo grau; 3. Compare usando as suas categorias, as trs definies apresentadas para hipo; 4. Apresente uma explicao para o erro produzido pela avaliao da expresso eq2,% 3#- .#- 5#- 5. Apresente uma seqncia de redues para a expresso: eq2,% 2#- 5#- 2#- 6. Apresente um conjunto de definies para a funo total para a determinao das razes de uma equao do segundo grau. 2.6. ASSINATURA DE FUNES E A NOTAO CURRY: Do estudo de funes sabemos que toda funo possui um domnio e um contradomnio. O domnio pode ser formado por zero ou mais conjuntos. Quando uma funo no possui domnio dizemos que uma funo constante. Para descrever este conhecimento sobre domnio e contradomnio usado o conceito de "assinatura, do ingls "signature. Para conhecer o tipo de uma funo, disponvel na biblioteca do HUGS ou construda pelo programador, basta usar, no ambiente HUGS, o comando: Hugs> :t <nome da funo> O quadro a seguir apresenta alguns exemplos. Hugs> :t sqrt sqrt :: Floating a => a -> a Hugs> :t sin sin :: Floating a => a -> a Hugs> :t abs abs :: Num a => a -> a Hugs> :t mod mod :: Integral a => a -> a -> a Hugs> :t div div :: Integral a => a -> a -> a O exemplo sqrt :: Floating a => a -> a, pode ser lido da seguinte maneira: "A funo sqrt mapeia um valor a do tipo FIoating em outro valor do tipo FIoating. Os termos :nte,ral, :nt, 6u!, ;loatin, etc sero cuidadosamente apresentados no Capitulo 5. Por enquanto, basta antecipar que cada valor, inclusive os numricos, podem ser de tipos diferentes e a linguagem prov uma classificao para esses valores. Podemos fazer uma analogia com a matemtica clssica onde os nmeros podem ser: naturais, inteiros, racionais, reais, irracionais, co!plexos etc. As linguagem de programao, por questes relacionadas com a tecnologia digital, podem "inventar outros tipos para facilitar a manipulao de dados pelos computadores digitais. 17 Vejamos um exemplo: na funo que determina a mdia aritmtica de 3 nmeros reais, temos que o domnio formado pelo produto cartesiano R x R X R e o contradomnio R. A assinatura desta funo a seguinte: ma3 :: R X R X R R Em Haskell poderamos ter a seguinte definio: ma3 x y z = (x + y + z) / 3. usual tambm dizer que R X R X R R o tipo da funo !a3. Para simplificar o trabalho com funes o matemtico Haskell Curry criou uma nova notao, que considera que qualquer funo tem sempre um nico parmetro de entrada. Segundo ele, o resultado de aplicar uma funo sobre um parmetro produz uma nova funo que pode ser aplicada a outro parmetro, e assim sucessivamente. Por exemplo, nesta notao, a funo !a3 teria a seguinte assinatura: ma3 :: R R R R Podemos ler da seguinte maneira: !a3 uma funo que mapeia um numero real em uma nova funo cuja assinatura : ma3 :: R R R. A funo ma3 por sua vez mapeia um nmero real em uma nova funo cuja assinatura : ma3 :: R R. A funo ma3 por sua vez mapeia um nmero real em uma nova funo cuja assinatura : ma3 :: R. Que uma funo constante. No exemplo acima, quando solicitamos a avaliao da expresso: ma3 3 4 5 o processo de avaliao pode ser entendido da seguinte maneira: ma3 3 4 5 "(x + y + z)/ 3 4 5 18 "( 3 + y + z)/ 3 4 5 "(3 + 4 + z)/3 5 "(3 + 4 + 5)/3 "(7 + 5)/3 "12/3 4 Podemos ainda, obter a definio de novas funes a partir de uma funo f previamente definida. f x y z = x + y + z g x y = f 3 x y h x = g 4 x m = h 5 No quadro a seguir mostramos a avaliao de tipos de cada uma das funes. Main> :t f f :: Num a => a -> a -> a -> a Main> :t g g :: Num a => a -> a -> a Main> :t h h :: Num a => a -> a Main> :t m m :: Integer Vejamos agora uma avaliao de expresses com estas funes: Main> (f 10 20 30) + (g 20 30) + (h 30) + m 162 Main> g 20 30 53 Main> f 10 20 30 60 Main> g 20 30 53 Main> h 30 37 Main> m 12 Exerccios 1) Avalie as expresses abaixo e apresente a sequncia de redues necessrias para a obteno do termo irredutvel (resultado final): a. mod 15 2 b. mod 15 2 + div 6 3 c. ma3 5 10 2 19 d. sqrt (15 2*3) / (17 12) 2) Escreva um arquivo texto (script) contendo a definio das funes abaixo. Use, quando for adequado, definies locais: a. Determinao da rea de um retngulo de lados a e b b. Determinao da rea de um crculo de raio r c. Determinao da mdia aritmtica de trs nmeros a, b e c 3. A ARTE DE RESOLVER PROBLEMAS 3.1. INTRODUO O grande objetivo deste curso criar oportunidades para que o estudante desenvolva suas habilidades como resolvedor de problemas. Mais especificamente estamos interessados na resoluo de problemas usando o computador, entretanto, temos certeza, que as idias so gerais o suficiente para ajudar a resolver qualquer problema. Embora muitas pessoas j tenham discutido sobre este assunto ao longo da histria, nosso principal referencial ser o professor George Polya, que escreveu um livro sobre este assunto, com o mesmo nome deste captulo, voltado para a resoluo de problemas de matemtica do ensino fundamental. Todos os alunos deste curso tero grande proveito se lerem este livro. As idias ali apresentadas com certeza se aplicam com muita propriedade resoluo de problemas com o computador. Na medida do possvel estaremos apresentando aqui algumas adaptaes que nos parecem apropriadas neste primeiro contato com a resoluo de problemas usando o computador. 3.2. DICAS INICIAIS Apresenta-se a seguir algumas orientaes: 3.2.1. S se aprende a resolver problemas atravs da experincia. Logo o aluno que est interessado em aprender deve trabalhar, buscando resolver por si mesmo, os problemas propostos antes de tentar o auxlio do professor ou de outro colega; 3.2.2. A ajuda do professor no deve vir atravs da apresentao pura e simples de uma soluo. A ajuda deve vir atravs do fornecimento de pistas que ajudem o aluno a descobrir a soluo por si mesmo; 3.2.3. muito importante no se conformar com uma nica soluo. Quanto mais solues encontrar, mais hbil o estudante ficar, e alm disso, poder comparar as vrias alternativas e escolher a que lhe parecer mais apropriada. A escolha dever sempre ser baseada em critrios objetivos. 3.2.4. Na busca pela soluo de um problema, nossa ferramenta principal o questionamento. Devemos buscar formular questes que nos ajudem a entender o problema e a elaborar a soluo. 3.2.5. Aprenda desde cedo a buscar um aprimoramento da sua tcnica para resolver problemas, crie uma sistematizao, evite chegar na frente de um problema como se nunca tivesse resolvido qualquer outro problema. Construa um processo individual e v aperfeioando-o cada vez que resolver um novo problema. 20 3.3 COMO RESOLVER UM PROBLEMA O professor Polya descreve a resoluo de um problema como um processo complexo que se divide em quatro etapas, as quais apresentamos aqui, com alguma adaptao. Segundo nos recomenda Polya, em qualquer destas etapas devemos ter em mente trs questes sobre o andamento do nosso trabalho, que so: Por onde comear esta etapa? O que posso fazer com os elementos que disponho? Qual a vantagem de proceder da forma escolhida? Etapa 1 - Compreenso do ProbIema impossvel resolver um problema sobre o qual no tenhamos um entendimento adequado. Portanto, antes de correr atrs de uma soluo, concentre-se um pouco em identificar os elementos do problema. Faa perguntas tais como: Quais so os dados de entrada? O que desejamos produzir como resultado? Qual a relao que existe entre os dados de entrada e o resultado a ser produzido? Quais so as propriedades importantes que os dados de entrada possuem? Etapa 2 - PIanejamento Nesta fase devemos nos envolver com a busca de uma soluo para o problema proposto. Pode ser que j o tenhamos resolvido antes, para dados ligeiramente modificados. Se no o caso, pode ser que j tenhamos resolvido um problema parecido. Qual a relao que existe entre este problema que temos e um outro problema j conhecido? Ser que podemos quebrar o problema em problemas menores? Ser que generalizando o problema no chegaremos a um outro j conhecido? Conhecemos um problema parecido, embora mais simples, o qual quando generalizado se aproxima do que temos? Etapa 3 - DesenvoIvimento Escolhida uma estratgia para atacar o problema, devemos ento caminhar para a construo da soluo. Nesta fase, devemos considerar os elementos da linguagem de programao que iremos usar, respeitando os elementos disponibilizados pela linguagem, tais como tipos, formas de definio, possibilidades de generalizao e uso de elementos anteriormente definidos. A seguir, devemos codificar cada pedao da soluo, e garantir que cada um esteja descrito de forma apropriada. Em outras palavras, no basta construir, temos que ser capazes de verificar se o resultado de nossa construo est correto. sto pode ser realizado pela identificao de instncias tpicas, da determinao dos valores esperados por estas, da submisso dessas instncias ao avaliador e finalmente, da comparao dos resultados esperados com os resultados produzidos pela avaliao de nossas definies. Alm disso, devemos garantir que a definio principal tambm est correta. Em sntese, a fase de desenvolvimento compreende as seguintes subfases: 1. construo da soluo; 2. planejamento do teste; 3. execuo manual do teste; 21 4. codificao da soluo; 5. teste com o uso do computador. Etapa 4 - AvaIiao do Processo e seus resuItados O trabalho do resolvedor de problemas no pra quando uma soluo est pronta. Devemos avaliar a qualidade da soluo, o processo que realizamos e questionar as possibilidades de uso posterior da soluo obtida e tambm do prprio mtodo utilizado. Novamente devemos fazer perguntas: Este foi o melhor caminho que poderia ter sido usado? Ser que desdobrando a soluo no obtenho componentes que poderei usar mais facilmente no futuro? Se esta soluo for generalizada possvel reus-la mais facilmente em outras situaes? Registre tudo, organize-se para a resoluo de outros problemas. Anote suas decises, enriquea a sua biblioteca de solues e mtodos. 3.4. UM PEQUENO EXEMPLO Enunciado: Deseja-se escrever um programa que permita determinar a menor quantidade de cdulas necessrias para pagar uma dada quantia em Reais. Etapa 1 - Compreenso Questo: Quais os dados de entrada? Resposta: A quantia a ser paga. Questo: Qual o resultado a ser obtido? Resposta: A menor quantidade de cdulas. Questo: Qual a relao que existe entre a entrada e a sada? Resposta: O somatrio dos valores de cada cdula utilizada deve ser igual quantia a ser paga. Questo: Existe algum elemento interno, ou seja, uma dado implcito?Resposta: Sim, os tipos de cdulas existentes.Considere que o Real possui apenas as seguintes cdulas: 1, 5, 10, 50 e 100. importante observar que qualquer cdula um mltiplo de qualquer uma das menores. Etapa 2 - PIanejamento Conheo algum problema parecido? Existe um problema mais simples? Podemos entender como um problema mais simples um que busque determinar quantas cdulas de um dado valor so necessrias para pagar a quantia desejada. Por exemplo, para pagar R$ 289,00 poderamos usar 289 cdulas de R$ 1,00. Ou tambm poderamos usar 5 notas de R$ 50,00, mas neste caso ficariam ainda faltando R$ 39,00. Claro que no queremos que falte nem que sobre e, alm disso, 22 desejamos que a quantidade seja mnima. Parece uma boa estratgia comear vendo o que d para pagar com a maior cdula e determinar quando falta pagar. O restante pode ser pago com uma cdula menor, e por a afora. Explorando a instncia do problema j mencionada, vamos fazer uma tabela com estes elementos. Expresso para determinar a quantidade de cdulas de um determinado valor. Quantidade de cdulas Quantia a Pagar 289,00 289 / 100 2 89,00 89/ 50 1 39,00 39/ 10 3 9,00 9/ 5 1 4,00 4/ 1 4 0,00 TOTAL 11
Etapa 3 - DesenvoIvimento Soluo 1 - Considerando a tabela acima descrita, codificando cada linha como uma subexpresso da definio. ncedulas q = (div q 100) + (div (mod q 100) 50) + (div (mod (mod q 100) 50) 10) + (div (mod (mod (mod q 100) 50) 10) 5)+ (div (mod (mod (mod (mod q 100) 50) 10) 5) 1) Soluo 2 - Considerando uma propriedade das cdulas, ou seja, j que uma cdula qualquer mltiplo das menores, a determinao do resto no precisa considerar as cdulas maiores do que a cdula que estamos considerando em um dado ponto. ncedulas2 q = (div q 100) + (div (mod q 100) 50) + (div (mod q 50) 10) + (div (mod q 10) 5)+ (div (mod q 5) 1) 23 Etapa 4 - AvaIiao A soluo deixa de explicitar as abstraes referentes quantidade de cdulas de um determinado valor, assim como o resto correspondente. Podemos questionar, no seria melhor explicitar? Assim poderamos us-las de forma independente e alm disso, a soluo fica mais clara e portanto inteligvel. ncedulas q = n100 q + n50 q + n10 q + n5 q + n1 q n100 q = div q 100 r100 q = mod q 100 n50 q = div (r100 q) 50 r50 q = mod (r100 q) 50 n10 q = div (r50 q) 10 r10 q = mod (r50 q) 10 n5 q = div (r10 q) 5 r5 q = mod (r10 q) 5 n1 q = div (r10 q) 5 Supondo que no queremos generalizar todas as funes menores ainda poderamos escrever o programa usando definies locais. ncedulas q = n100 + n50 + n10 + n5 + n1 where
n100 = div q 100 r100 = mod q 100 n50 = div r100 50 r50 = mod r100 50 n10 = div r50 10 r10 = mod r50 10 n5 = div r10 5 n1 = mod r10 5
Podemos ainda considerar que se houver uma troca no sistema, de modo a incluir uma nova cdula que no seja mltiplo de seus valores menores. Seria fcil mudar o programa para contemplar a mudana nas leis do mundo do problema. 3.5. PROVRBIOS O professor Polya tambm nos sugere que a lembrana de alguns provrbios pode ajudar o aprendiz (e o resolvedor de problemas) a organizar o seu trabalho. Diz Polya que, apesar dos provrbios no se constiturem em fonte de sabedoria universalmente aplicvel, seria uma pena desprezar a descrio pitoresca dos mtodos heursticos que apresentam. Alguns so de ordem geral, tais como: / fim indica os meios 24 0eus mel&ores amigos so / que1 ,or que1 /nde1 2uando e 3omo pergunte / que1 pergunte ,or que1 pergunte /nde1 pergunte 2uando e pergunte 3omo - e no pergunte a ningu4m quando precisar de consel&o 5o confie em coisa alguma1 mas s6 du7ide daquilo que merecer d87ida /l&e em torno quando encontrar o primeiro cogumelo ou fi$er a primeira descoberta9 ambos surgem em grupos
A seguir apresentamos uma lista deles, organizados pelas etapas s quais parecem mais relacionados. Etapa 1 : Compreenso do probIema. 2uem entende mal1 mal responde ,ense no fim antes de comear / tolo ol&a para o comeo1 o s:bio 7; o fim / s:bio comea pelo fim1 o tolo termina no comeo Etapa 2 : PIanejamento da soIuo. < perse7erana 4 a me da boa sorte 5o se derruba um car7al&o com uma s6 mac&adada 0e no princ=pio no conseguir1 continue tentando >xperimente todas as c&a7es do mol&o ?ele@a-se conforme o 7ento Aaamos como pudermos se no pudermos fa$er como queremos / s:bio muda de opinio1 o tolo nunca Banten&a duas cordas para um arco Aaa e refaa que o dia 4 bastante longo / ob@eti7o da pescaria no 4 lanar o an$ol mas sim pegar o peixe / s:bio cria mais oportunidades do que as encontra / s:bio fa$ ferramentas daquilo que l&e cai Cs mos Aique sempre de ol&o na grande ocasio Etapa 3: Construo da soIuo. /l&e antes de saltar ,ro7e antes de confiar Dma demora prudente torna o camin&o seguro 2uem quiser na7egar sem risco1 no se faa ao mar Aaa o que puder e espere pelo mel&or E f:cil acreditar naquilo que se dese@a 25 Fegrau a degrau sobe-se a escada / que o tolo fa$ no fim1 o s:bio fa$ no princ=pio Etapa 4: AvaIiao da soIuo. 5o pensa bem quem no repensa E mais seguro ancorar com dois ferros Exerccios 1) Compare as duas definies para a funo que descreve o nmero mnimo de cdulas para pagar uma quantia q, ncedulas e ncedulas2. Discuta; 2) Desenvolva a soluo para o problema da cdulas considerando o fato de que o Real possui notas de 2 e notas de 20. O que muda? 3) Apresente trs problemas semelhante ao das cdulas; 4) Desenvolva uma soluo para o problema considerando que para cada tipo de cdula existe um quantidade limitada (maior ou igual a zero); 26 4. ABSTRAO, GENERALIZAO, INSTANCIAO E MODULARIZAO 4.1. INTRODUO Na busca por resolver um problema podemos usar vrios princpios, cada um evidentemente ter uma utilidade para a resoluo do problema, ou para garantia de sua correo ou ainda para facilitar os usos posteriores da soluo que obtivermos. Apresentamos a seguir alguns deles. 4.2. ABSTRAO Quando escrevemos uma expresso e no damos nome a ela, o seu uso fica limitado quele instante especfico. Por exemplo, suponha que desejamos determinar a hipotenusa de um tringulo retngulo com catetos 10 e 4. Como conhecemos o teorema de Pitgoras (a 2 = b 2 + c 2 ), podemos usar diretamente nossa mquina funcional para avaliar a seguinte expresso:
> sqrt ((10.0 * 10.0)+ (4.0 * 4.0)) 10.7703 A expresso que codificamos serve apenas para esta vez. Se em algum outro instante precisarmos avali-la, teremos que codific-la novamente. Para evitar isso, que damos nomes s nossas expresses, para que possamos us- las repetidamente, apenas referenciando-as pelo seu nome. No caso acima, poderamos escrever a definio: hipotenusa = sqrt ((10.0 * 10.0)+ (4.0 * 4.0)) De posse dessa definio nossa mquina poder avaliar a expresso sempre que dela precisarmos. Basta escrev-la: > hipotenusa 10.7703 Voc pode agora estar se perguntando, porque no trocamos a definio para usar diretamente o valor %-#<<-3? hipotenusa = %-#<<-3 Agindo assim a mquina no precisaria avaliar a expresso sqrt ((%-#- ) %-#-)+ (.#- ) .#-)) a cada uso. Por outro lado no ficaria registrada a origem do valor %-#<<-3, com o tempo perderamos esta informao. De qualquer forma, teramos criado uma abstrao qual denominamos 7ipotenusa. Outra pergunta pode estar rondando a sua cabea, por que escrever uma definio que sempre avaliada para o mesmo valor, por que no generaliz-la? Bom, este foi apenas um recurso didtico para separar os dois conceitos: a abstrao que acabamos de apresentar e a generalizao que apresentaremos a seguir. No entanto convm lembrar que algumas definies so realmente constantes e que so de grande utilidade, como o caso da seguinte definio: 27 pi = 3.1416 4.3. GENERALIZAO Quando uma abstrao se aplica a vrios valores podemos generaliz-la. Assim, alm de us-la vrias vezes para os mesmos valores, podemos tambm us-la para valores diferentes. Esta ultima alternativa evidentemente facilita o seu reuso. Vamos apresentar duas formas para fazer isso: a) Elaborando a definio usando outras definies constantes. Por exemplo, no caso da definio acima para a hipotenusa, poderamos escrever as definies a seguir: b = 10.0 c = 4.0 hipotenusa = sqrt (( b * b) + ( c * c)) Aqui, 7ipo se aplica a dois valores quaisquer + e c, os quais so objetos de outras definies. b)Uma forma mais geral atravs do conceito de parametrizao. Esta consiste em indicar no cabealho da definio (lado esquerdo da igualdade) quais so os objetos da generalizao. Para o exemplo que estamos trabalhando, podemos escrever: hipotenusa b c = sqrt (( b * b) + ( c * c)) Temos ento descrito a funo paramtrica 7ipotenusa cujos parmetros so + e c. 4.4 INSTANCIAO A parametrizao permite que usemos a mesma definio para diferentes instncias do problema. Por exemplo, suponha que desejamos determinar a hipotenusa de trs tringulos retngulos. O primeiro com catetos 10 e 4, o segundo com catetos 35 e 18 e o terceiro com catetos 9 e 12. Podemos instanciar a definio paramtrica para estabelecer a seguinte interao: > hipo 10.0 4.0 10.7703 > hipo 35.0 18.0 39.3573 > hipo 9.0 12.0 15.0 4.5. MODULARIZAO Em geral, nossos problemas no sero to simples e diretos quanto o exemplo acima. Quando nos deparamos com problemas maiores, um bom princpio : =i$ida para facilitar a conquista# Basicamente este princpio consiste em quebrar o problema inicial em problemas menores, elaborar a soluo para cada um dos problemas menores e depois combin-las para obter a soluo do problema inicial. A cada um dos subproblemas encontrados 28 podemos reaplicar o mesmo princpio. Segundo Polya, esta uma heurstica muito importante qual ele denomina de Decomposio e Combinao. Antes de pensar em codificar a soluo em uma linguagem de programao especfica, podemos represent-la atravs de um esquema grfico denominado de estrutura modular do problema (veja a representao para o exemplo a seguir). 4.5.1 MODULARIZANDO: Considere o problema de descrever a rea da Figura 4.1 abaixo. Figura 4.1 Um polgono irregular para o qual desejamos determinar a sua rea Como podemos concluir por uma inspeo da figura, no existe uma frmula pronta para calcular sua rea. Precisamos dividir a figura em partes para as quais conheamos uma maneira de calcular a rea e que, alm disso, conheamos as dimenses necessrias. Podemos fazer vrias tentativas, como por exemplo, as ilustradas nas Figuras 4.2 a, 4.2 b e 4.2 c. 29 a b c Figura 4.2 Possveis subdivises do polgono apresentado na Figura 4.1. Podemos tentar descrever a rea das figuras menores em cada uma das figuras apresentadas. Nas Figuras 4.2.b e 4.2c, parece que precisaremos subdividir novamente. Em 4.2 b a "casinha pode ser transformada em um retngulo e um tringulo. Em 4.2 c a "casinha azul (meia-gua) que pode ser dividida em um retngulo e um tringulo, como na Figura 4.3. E a Figura 4 a? Que podemos dizer? Figura 4.3 Subdiviso de uma sub!igura da Figura 4.3 "# Vamos partir para a nossa soluo a partir da figura Fig. 4.2c. Podemos dizer que a rea total pode ser obtida pela soma das reas amarela, vermelha e azul. A rea azul pode ser subdividida em azul-claro e azul-escuro (ver Figura 4.3).
rea total = rea amarela + rea vermelha + rea azul rea azul = rea azulclaro + rea azulescuro
Quando escolhemos as reas acima citadas, no foi por acaso. A escolha foi baseada na simplicidade do subproblema. Podemos usar este conhecimento para chegar a um outro. Trs das reas que precisamos calcular so de forma retangular. Ou seja, so especializaes de um conceito mais geral. A quarta rea, tambm pode ser obtida pela especializao do conceito de tringulo retngulo. Vamos agora aproximar nossas definies da linguagem de programao. Portanto podemos escrever: 30 a_retanguIo x y = x * y O tringulo que temos retngulo e podemos descrever sua rea por: a_t_retanguIo x y = (x * y) / 2.0 A determinao da rea vermelha nos traz uma pequena dificuldade. No nos foi informado qual o comprimento da base do retngulo. E agora? Observando bem a figura podemos concluir que a base do retngulo igual hipotenusa do tringulo retngulo de catetos a e d# Podemos escrever ento: atotal a b c d e = a_retangulo a b + a_retangulo (hipo a d) e + a_azul a c d a_azul x y z = a_retangulo y z + a_t_retangulo x y hipo x y = sqrt ( x * x + y * y) a_retangulo x y = x * y a_t_retangulo x y = (x * y) / 2.0
A estrutura da soluo de um problema obtida pela modularizao pode ser representada por um diagrama denominado rvore. Para o problema acima discutido a rvore da forma apresentada na Figura 4.5. Figura 4.5 Representao em rvore da estrutura modular do problema "clculo da rea do polgono irregular apresentado na Figura 4.1. 4.6. UM EXEMPLO DETALHADO Problema: Escreva uma descrio funcional para o volume de cada uma das peas apresentadas nas Figuras 4.6 e 4.7. 31 Figura 4.$ %e&a no. 1 Figura 4.' %e&a no. ( SoIuo 1: Vamos comear pela Figura 4.6. Uma rpida anlise nos leva a identificar duas partes. Na parte inferior temos um paraleleppedo, sobre o qual se assenta um cilindro. Vamos supor que so macios. Chamemos de a, b e c as dimenses do paraleleppedo, de r o raio da base do cilindro e de & a sua altura. O volume total da pea pode ser determinado pela soma do volume das duas peas menores. Chamemos a funo de volfig46. Uma possvel definio para essa funo :
voIfig46 a b c r h = (a * b * c) + (3.1416 * r * r * h) Vamos ento tratar da pea descrita na figura 4.7. Agora identificamos uma pea apenas. Um paraleleppedo com um furo no centro. Chamemos de a, b e c as dimenses do paraleleppedo, de r o raio do buraco. Para descrever o volume da pea devemos subtrair do volume do paraleleppedo o volume correspondente ao buraco. Chamemos a funo de volfig47. Uma possvel definio para volfig47 :. voIfig47 a b c r = (a * b * c) - (3.1416 * r * r * c) SoIuo 2: A soluo 1, apesar de resolver o problema, deixa de contemplar algumas prticas importantes. No tratamento da primeira pea (Figura 4.6), apesar de identificadas duas peas menores, estas abstraes no foram descritas separadamente. Ou seja, deixamos de registrar formalmente a existncia de duas abstraes e com isso no pudemos modularizar a descrio da funo principal. Podemos tentar ento um outro caminho, contemplando as abstraes para o cilindro e para o paraleleppedo: volcil r h = 3.1416 * r * r * h volpar a b c = a * b * c Volfig46 a b c r h = volpar a b c + volcil r h Voltemos ento para a segunda pea (Figura 4.7), e vejamos se podemos identificar similaridades. Aqui s temos uma pea, que se parece com o paraleleppedo da primeira figura. Como podemos identificar similaridades? Aprofundando melhor nossa anlise podemos lembrar que o furo no paraleleppedo, corresponde a um cilindro que foi retirado. Desta forma, podemos ento concluir que o volume da figura 4.7 pode ser obtido pela diferena entre o volume de um paraleleppedo e de um cilindro. Como j temos as definies para volume de cilindro e volume de paraleleppedo, s nos resta escrever a definio final do volume da figura 4.7:
voIfig47 a b c r = volpar a b c - volcil r c 32 Analisando a soluo, podemos tirar algumas concluses: a) a soluo ficou mais clara, b) a soluo propiciou o reaproveitamento de definies. c) se precisarmos usar volume de cilindro e de paraleleppedo isoladamente ou em outras combinaes, j as temos disponveis. Consideraes compIementares - As descries das reas de um retngulo e de uma circunferncia podem ser dadas respectivamente por:
acir r = pi * r * r aret a b = a * b Desta forma, podemos reescrever o volume do cilindro e do paraleleppedo da seguinte maneira: volcil r h = acir r * h volpar a b c = aret a b * c SoIuo 3: Se aprofundarmos a anlise podemos observar que as duas figuras podem ser abstradas como uma s! O cilindro que aparece na primeira, como um volume que deve ser acrescentado pode ser entendido como o mesmo cilindro que deve ser subtrado na segunda. Alm disso, podemos estender a soluo para furos que no vazem a pea, pois a altura do cilindro pode ser qualquer. Considere a definio a seguir:
voIfig a b c r h = volpar a b c + volcil r h Podemos observar que esta a mesma definio apresentada anteriormente para a Figura 4.6. O que muda o uso. Para calcular o volume da Figura 4.6 usamos um 7 positivo e para Figura 4.7 um 7 negativo. Observe os exemplos a seguir: > volfig 6.0 8.0 2.0 2.0 5.0 158.832 -- determina o volume de uma instncia da figura Fig. 4.6, com um cilindro de raio 2 e altura 5. > volfig 6.0 8.0 2.0 2.0 (-2.0) 70.8673 -- determina o volume de uma instncia da figura Fig. 4.7, vasada por um furo de raio 2. -- neste caso fornecemos um valor negativo para a altura do cilindro com o mesmo valor da altura do paraleleppedo > volfig 6.0 8.0 2.0 2.0 (-1.0) 83.4336 -- determina o volume de uma instncia da figura Fig. 4.7, que tem um furo de raio 2 e profundidade 1. 33
Exerccios: 1) Discuta o significado da expresso voIfig 6.0 8.0 2.0 2.0 (-5.0). O que poderia ser feito para contornar este efeito indesejvel? 2) Resolva o problema da Figura 4.1 usando a modularizao sugerida pela figura 4.2 b. 3) Redesenhe a Figura 4.5 (rvore de modularizao) para a soluo apresentada ao exerccio 2. 4) Apresente 3 problemas similares para clculo de rea de polgono irregular. 5) Apresente 3 problemas similares para clculo de volume de objetos. 6) Apresente 3 problemas similares em outros domnios de conhecimento. 34 5. TIPOS DE DADOS NUMRICOS 5.1. INTRODUO Denominamos Tipo de Dados a um conjunto de valores, munido de um conjunto de operaes sobre esses valores. Por exemplo, podemos denominar de T1 ao tipo de dados formado por um conjunto S de valores idntico aos nmeros naturais (S = {0,1,2,3, ...}) e munido das operaes de adio (a) e multiplicao (!). Cada operao possui, por sua vez, um tipo, indicando o domnio e o contradomnio. Por exemplo, para o tipo T1, o domnio de a > ? > e o contradomnio ># Como visto anteriormente, a notao a seguir usualmente utilizada e em geral denominada de "assinatura da operao. a :: S X S S m :: S X S S Como visto no Captulo 2, na notao Curry escreveramos: a :: S S S m :: S S S Em Haskell, conhecendo-se o tipo das operaes e funes que compem uma expresso podemos determinar o tipo do valor que dela resultar, ou seja, o seu contra- domnio. Em linguagens de programao isto equivale a dizer que a linguagem forte!ente tipada. Dizemos ainda que os tipos so elementares ou estruturados. Os numricos so elementares, assim como tambm o so os tipos lgico e os caracteres. Neste captulo trataremos dos tipos numricos, os demais viro em captulos posteriores. Os nmeros formam um tipo de dados fundamental na computao. Aqui nos interessa subdividi-los em inteiros e reais. Antes de irmos alm, importante que se saiba que, sendo o computador composto de elementos finitos, algumas adaptaes e simplificaes precisam ser feitas para o tratamento de nmeros. Em HUGS, quando estamos submetendo expresses para avaliao, podemos desejar que o tipo do resultado seja exibido. Para que isso ocorra podemos usar o comando :set, que ativa ou desativa parmetros do ambiente. Para ativar um parmetro usamos uma letra correspondente ao parmetro, precedido do smbolo "+. Para desativar usamos o smbolo "-". Para saber sobre outros parametros, experimente submeter apenas o comando ":set. A seqncia de interaes a seguir ilustra a ativao e desativao da exibio dos tipos dos resultados das avaliaes. > :set +t > 2^20 1048576 :: Integer > :set -t > 2^100 1267650600228229401496703205376 Os parmetros do interpretador, ativados pelo comando :set, so vlidos apenas enquanto a sesso do HUGS estiver ativa. 35 5.2. NMEROS INTEIROS Para trabalhar com nmeros inteiros, a linguagem Haskell prov o tipo Integer, que pode produzir nmeros com uma quantidade ilimitada de algarismos. Entretanto, como a memria do computador finita, qualquer que seja a mquina real que estivermos usando, inevitavelmente esbarrar em limites. Na ilustrao a seguir podemos observar essa flexibilidade, quando obtemos o valor para a expresso 2 1000 , um nmero de 302 algarismos. Claro, o limite pode estar bem longe e podemos no ating-lo em nossas aplicaes. > 2^1000 1071508607186267320948425049060001810561404811705533607443750388370351051 1249361224931983788156958581275946729175531468251871452856923140435984577 5746985748039345677748242309854210746050623711418779541821530464749835819 4126739876755916554394607706291457119647768654216766042983165262438683720 5668069376 Experimente com nmeros maiores! Por exemplo, 9999 9999 um nmero que tem por volta de 40 mil algarismos. provida ainda uma representao de inteiros mais restrita, denominada :nt. Esta verso trabalha com um intervalo de valores fixo e reduzido e tem como vantagem economizar memria do computador e tempo de processamento, usando caractersticas especficas do computador. Para que um nmero seja representado nesta verso, devemos indicar explicitamente, como no exemplo a seguir. > 1234567890::Int 1234567890 :: Int
> 12345678901::Int Program error: arithmetic overflow O exemplo a seguir ilustra a diferena entres as duas possibilidades. > 1089979879870979879 1089979879870979879 :: Integer > 1089979879870979879::Int Program error: arithmetic overflow O conjunto de operaes associadas ao domnio pode variar. Em geral so fornecidas as seguintes operaes primitivas: Nome Descrio Exemplos + Adio > 13 + 15 + 21 + 24 + 27 + 31 131 :: nteger * Multiplicao > 20 * 10 * 98 19600 :: nteger - Subtrao > 1234 - 4321 -3087 :: nteger 36 div,quot diviso inteira > div 12834 10 1283 :: nteger ^ Potncia > 2^20 1048576 :: nteger rem resto da diviso inteira entre dois inteiros > rem 12834 10 4 :: nteger > rem (12834 10) -4 :: nteger mod mdulo da diviso inteira entre dois inteiros > mod 12834 10 4 :: nteger > mod (-12834) 10 6 :: nteger abs valor absoluto > abs 123 123 :: nteger > abs (-123) 123 :: nteger signum produz -1, 0 ou 1, indicando quando o nmero negativo, zero ou positivo > signum (-3888876527829798) -1 :: nteger > signum 0 0 :: nteger > signum 3 1 :: nteger
Algumas observaes so importantes: a) As operaes podem ser combinadas para construir expresses mais complexas; > 5 + 12 * 3 41 :: nteger b) As operaes possuem precedncia, ou seja, existe uma ordem em que devem ser consideradas. No exemplo anterior, a multiplicao (*) tem precedncia sobre a adio (+) e portanto realizada antes da adio. Esta precedncia pode ser modificada pelo uso de parntesis. > (5 + 12) * 3 51 :: nteger c) A operao div (diviso) parcial, ou seja, no se aplica quando o denominador nulo. Quando submetemos uma expresso com esta caracterstica, o HUGS no avalia a expresso e sinaliza que h um erro. > div 398 0 Program error: divide by zero 37 5.3. NMEROS REAIS O tipo em Haskell para representao dos nmeros reais nos computadores denominado ;loat. Para este tipo no falaremos de uma lista de constantes. Ao invs disso falaremos aqui em magnitude e preciso de um nmero. Como na notao cientfica. A preciso nos diz quantos algarismos significativos so usados e a magnitude nos diz qual o maior expoente admitido. Por exemplo, uma determinada implementao pode utilizar 6 algarismos significativos. Nesta, o nmero 123456.789 seria representado pelo nmero 123457.0, onde o 6 foi arredondado para 7. A magnitude permite a representao de nmeros bem pequenos e bem grandes. Por exemplo, um nmero muito grande como 99999999999999999999999999999 poderia ser representado por 1.0e+29 e um nmero bem pequeno como 0.00000000009999999999999999999999999999, poderia ser representado por 1.0e-010. Veja os exemplos a seguir: > 0.1234567890123456789012345678901234567890 0.123456789012346 :: DoubIe A representao cientfica utilizada quando necessrio: > 1234567890123456789012345678.9 1.23456789012346e+027 :: DoubIe A implementao corrente do HUGS usa uma verso estendida do tipo Float, denominada DoubIe. Se desejarmos, podemos representar os nmeros usando uma quantidade menor de algarismos significativos, para isso precisamos explicitar que o nmero considerado deve ser representado como do tipo FIoat. > 0.1234567890123456789012345678901234567890::FIoat 0.1234568 :: FIoat O nmero pi definido com uma constante do tipo Double. Se houver necessidade do uso do valor de pi nas expresses descritas no Haskell, basta escrever a palavra pi, que ser avaliada como a constante 3,14159... Veja o exemplo a seguir: > pi 3.14159265358979 :: Double O conjunto de operaes aqui tambm pode variar. Em geral, as listadas no quadro abaixo so providas. 38 Nome Descrio ExempIos + Adio > 2.5 + 3.3216 + 0.389458 6.211058 :: Double * Multiplicao > 3.2 * 1.23 * 0.208 0.818688 :: Double - Subtrao > 3.456789 - 1.344499089877 2.112289910123 :: Double / Diviso > 9.345988 / 234569.34 3.98431781408431e-005 :: Double ^ potncia (o expoente tem que ser nt e positivo) > 1.5324^10 71.4038177956654 :: Double sin Seno > sin pi 1.22460635382238e-016 :: Doubl cos Coseno > cos pi -1.0 :: Double tan Tangente > tan ( pi / 4.0) 1.0 :: Double sqrt raiz quadrada > sqrt 8.5 2.91547594742265 :: Double log logaritmo na base e > log 2.71828182845905 1.0 :: Double logBase logaritmo na base escolhida >logBase 10 2 0.301029995663981 :: Double exp potncia na base e > exp 1.0 2.71828182845905 :: Double
Da mesma forma como ocorre nos nmeros inteiros, nos reais tambm possvel combinar operaes para construir expresses mais complexas. Tambm vale para os reais a precedncia de operadores. Como nos inteiros podemos usar parntesis para controlar a ordem em que as operaes sero realizadas. 5.4. CONVERSO DE TIPOS Em Haskell os inteiros so tratados como um subconjunto dos reais. Assim, quando temos nmeros reais misturados com nmeros inteiros em uma mesma expresso, os nmeros inteiros so automaticamente tratados como reais. > 3 + 5 8 :: nteger > 3 + 5.0 8.0 :: Double Existem funes especficas para converso de tipos: 39 a) a funo truncate converte um real x para o menor inteiro menor ou igual x. > truncate pi 3 :: nteger b) a funo round converte um real x para o inteiro mais prximo de x, ou seja: round x = truncate (x + 0.5) > round pi 3 :: nteger > round (exp 1) 3 :: nteger Outras exemplos de funes que existem s com o intuito de converso de tipos so fromnteger e fromRational. As assinaturas delas so: fromnteger :: (Num a) => nteger -> a fromRational :: (Fractional a) => Rational -> a 5.5. PRECEDNCIA DOS OPERADORES Quando aparecem vrios operadores juntos na mesma expresso, certas regras de precedncia so estabelecidas para resolver possveis ambigidades. A ordem em que os operadores so considerados a seguinte: 1) di7, mod, abs, sqrt e qualquer outra funo 2) ^ 3) * / 4) +, - Da mesma forma que na matemtica usual podemos usar os parntesis para forar uma ordem diferente de avaliao. ExempIos: > 2 + 3 * 5 17 O operador * tem precedncia sobre o operador +. Podemos escrever a expresso a seguir para denotar a seqncia de avaliaes: 40 2 + 3 * 5 2 + 15 17 > (2 + 3) * 5 25 A ordem de avaliao foi alterada pelo uso dos parntesis. A seqncia de avaliao portanto: (2 + 3) * 5 5 * 5 25 > 3 * mod 10 4 + 5 11 A prioridade para avaliao do operador mod, seguida da avaliao do operador * e finalmente do operador +. Podemos escrever a seguinte seqncia de avaliao: 3 * mod 10 4 + 5 3 * 2 + 5 6 + 5 11 > 3 ^ mod 10 4 9 A primeira avaliao do operador mod e em seguida avaliado o operador ^. A seqncia de avaliao : 3 ^ mod 10 4 3 ^ 2 9 > 4 ^ mod (div 20 4) 2 4 A seqncia de avaliao : 4 ^ mod (div 20 4) 2 4 ^ mod 5 2 4 ^ 1 1 5.6. ORDEM DE ASSOCIAO Quando h ocorrncia de operadores de mesma precedncia leva-se em considerao a ordem de associao que pode ser direita ou esquerda. 1. O operador de subtrao faz associao esquerda, 5 - 4 - 2 = (5 - 4) - 2 e no 5 - ( 4 - 2) 2. J o operador de exponenciao faz associao direita, 3 ^ 4 ^ 5 = 3 ^ ( 4 ^ 5 ) e no ( 3 ^ 4 ) ^5 41
O uso adequado das noes de precedncia e associao serve tambm para escrevermos expresses com economia de parntesis. Observaes: a) O operador unrio - deve ser sempre representado entre parnteses quando utilizado junto com outro operador: (- x)^y ou - (x^y) e nunca -x^y ou x^-y b) A exponenciao, quando repetida em uma expresso, avaliada da direita para a esquerda: 2^3^3 = 2^(3^3) c) Os demais operadores, na situao acima, so avaliados da esquerda para a direita: 2 - 3 - 5 = (2 - 3) 5 e 2 + 3 + 3 = (2 + 3) + 3 ExempIos: >3 - 7 - 2 - 6> 3 * 7 + 4 25 > 3 * ( 7 + 4) 33 > 3 ^ ( 1 + 3) 81 5.6. TIPOS DE NOVAS DEFINIES DE FUNES Ao definir novas funes, o fazemos tomando por base os tipos de dados existentes na linguagem. O corpo dessas definies tem por base a composio das operaes fornecidas por estes tipos bsicos. Assim, o tipo do dado resultante da avaliao de uma aplicao desta nova funo, pode ser antecipado por um analisador de tipos, antes mesmo de avaliar a expresso. Observe as duas definies a seguir: mediaA x y = (x + y) / 2 e mediaB x y = truncate ((x + y) / 2) O tipo de dado resuItante da apIicao da funo mediaA reaI, uma vez que a operao "/" (diviso) s se apIica a nmeros reais. Da mesma forma, o tipo resuItante da apIicao da funo mediaB inteiro, dado que a Itima operao reaIizada (truncate), converte reais para inteiros. Confira nas avaIiaes a seguir: > mediaA 3 4 3.5 :: Double > mediaB 3 4 3 :: nteger 42 5.7. HIERARQUIA DE TIPOS At agora dissemos que a linguagem Haskell trabalha com dois tipos numricos, os reais e os inteiros. sto uma verdade irrefutvel. Ao avaliar uma expresso numrica o resultado ser sempre um nmero real ou um nmero inteiro. Entretanto, para viabilizar as converses automticas de tipo, a linguagem Haskell realiza os nmeros atravs de uma hieraquia de classes. A classe numrica mais geral Num, dela derivam as classes ReaI e FractionaI. De ReaI derivam IntegraI e ReaIFrac. De FractionaI derivam ReaIFrac e FIoating. De ReaIFrac e de FIoating deriva ReaIFIoating. Algumas classes derivam de mais de uma classe, ao que denominado de herana mltipla, como o caso de ReaIFrac que deriva das classes ReaI e FractionaI. A Figura 5.1 apresenta a hierarquia completa. Podemos observar aqui trs classes de apoio, usadas para estruturar as derivaes, Ord, Eq e Enum. Aqui devemos entender classe como uma coleo de operadores sobre um determinado conjunto de valores. Uma classe derivada herda todas as funes providas por sua ancestral e pode acrescentar novas. Figura 5.1 Hierarquia de Tipos 5.7.1 Eq 43 Eq Ord Num ReaI FractionaI Enum IntegraI ReaIFrac FIoating ReaIFIoating A classe Eq introduz na linguagem a possibilidade de fazer-se distino entre os valores de um determinado tipo, sendo a classe mais bsica. Com valores que so instncias desta classe podemos aplicar as operaes de igualdade e desigualdade. Podemos pensar como se esta classe tivesse como membros todos os valores sobre os quais se pode fazer distino. O tipo Bool ser apresentado em detalhes no Captulo 6. funes assinatura descrio informaI (==) a -> a -> Boo l Avalia se dois valores so idnticos (/=) a -> a -> Boo l Avalia se dois valores so distintos Observemos que a comparao sempre realizada com valores do mesmo tipo. ExempIos: Hugs> 3 == 3.0 True :: Bool Hugs> 3.0 /= 3 False :: Bool Hugs> 'a' == 'a' True :: Bool Hugs> 'a' == 'b' False :: Bool Hugs> 'a' == head "abacate" True :: Bool Hugs> [1,2,3]==[1..3] True :: Bool Hugs> [1,2,3]==['1', '2', '3'] ERROR - Cannot infer instance *** Instance : Num Char *** Expression : [1,2,3] == ['1','2','3'] Hugs> 3 /= 'a' ERROR - Cannot infer instance *** Instance : Num Char *** Expression : 3 /= 'a' Hugs> 3.0 == 2.999999999999999999999999999999 True :: Bool Hugs> 2.9999999999999999999999999999999 == 2.9999999999999999 True :: Bool Hugs> 2.9999999999999999999999999999999 == 2.999999999999999 False :: Bool Observemos que: a) A comparao sempre realizada com valores do mesmo tipo. Nos exemplos acima, a tentativa de comparar valores de tipos diferentes resulta em uma mensagem de erro, avisando que estamos tentando comparar um valor da classe Num com um valor da classe Char; 44 b) A igualdade de nmeros dependente de uma representao interna, como podemos observar pelos trs ltimos exemplos.
5.7.2 Ord Esta uma classe mais geral que as dos nmeros, provendo suporte para construo de outras classes, inclusive as classes numricas. A idia geral prover operaes para todos os conjuntos de valores que podem ser ordenados. funes assinatura descrio informaI compare a -> a -> Ordering Compara dois valores associando- os a constantes LT, EQ e GT compare x y = LT se x menor que y EQ se x igual a y GT se x maior que y (<), (<=), (>=), (>) a -> a -> Bool Operadores relacionais que compara dois valores resultando em um valor booleano (True ou False) max a -> a -> a max x y = x se x maior ou igual a y y em caso contrrio min a -> a -> a min x y = x se x menor ou igual a y y em caso contrrio ExempIos: Hugs> compare 3 4 LT :: Ordering Hugs> min 3 4 3 :: nteger Hugs> min 4 3 3 :: nteger Hugs> max 4 3 4 :: nteger Hugs> max 3 4 4 :: nteger Hugs> 3 <=4 True :: Bool Hugs> compare 'g' 'b' GT :: Ordering Hugs> "abacaxi" > "abacate" True :: Bool Hugs> max "abacaxi" "melancia" "melancia" :: [Char] Hugs> min "abacaxi" "melancia" 45 "abacaxi" :: [Char] 5.7.3 Enum Esta uma classe que introduz funes para descrever e operar com seqncia de valores. funes assinatura descrio informaI succ, a -> a pred a -> a toEnum Int -> a fromEnum a -> Int enumFromThen a -> a -> [a] [n,n'..] enumFromTo a -> a -> [a] [n..m] enumFromThenTo a -> a -> a -> [a] [n,n'..m] Atemos-nos aqui a comentar as funes "succ e "pred, as demais sero apresentadas junto com listas, no capitulo que introduz este tipo de valores. A funo "succ determina o sucessor de um valor na seqncia de valores da qual faz parte e a funo "pred determina o seu predecessor. ExempIos: Hugs> succ (-5) -4 :: Integer Hugs> pred 0 -1 :: Integer Hugs> succ 0 1 :: Integer Hugs> pred (succ 0) == succ (pred 0) True :: Bool Hugs> pred 'a' '`' :: Char Hugs> pred 'b' 'a' :: Char Hugs> succ 'B' 'C' :: Char Hugs> succ '0' '1' :: Char Hugs> pred 1.0 0.0 :: Double Hugs> succ 0.99999 1.99999 :: Double 46 5.7.4 Num A classe Num a mais geral para os nmeros e prov as funes, (+), (-), (*), negate, signum e abs. funes assinatura descrio informaI (+), (-), (*) a -> a -> a Adio, subtrao e multiplicao usuais negate a -> a Define o simtrico de um nmero abs a -> a Define o valor absoluto de um nmero signum a -> a signum x = 1 se x positivo; -1 se x negativo e 0 se x nulo fromInteger Integer -> a Converte um valor do tipo Integer em um valor do tipo Num Na prtica isto significa que qualquer nmero usado em Haskell pode ser operado por estas funes. ExempIos: Hugs> 3 + 4 7 :: Integer Hugs> 3.0 + 4.0 7.0 :: Double Hugs> :t 3 + 4 3 + 4 :: Num a => a Hugs> :t 3.0 + 4.0 3.0 + 4.0 :: Fractional a => a Hugs> 3 + 4 7 :: Integer Hugs> 3.0 + 4.0 7.0 :: Double Hugs> negate 3 -3 :: Integer Hugs> negate 4.0 -4.0 :: Double Hugs> signum (-3) -1 :: Integer 5.7.5 ReaI A classe ReaI possui uma herana mltipla, descendendo das classes Num e Ord. sto significa que com um valor desta classe podemos operar tanto com as operaes da classe Num quanto com as operaes providas por Ord. A classe Real introduz a funo toRational para converso de um valor do tipo ReaI em um valor do tipo RationaI. funes assinatura descrio informaI toRational a -> Rational toRational x = p % q onde p e q so nmeros inteiros e x o cociente de p dividido por q 47 ExempIos: Hugs> toRational 5.5 11 % 2 :: Ratio Integer Hugs> toRational 1.25 5 % 4 :: Ratio Integer Hugs> toRational 0.75 3 % 4 :: Ratio Integer Hugs> toRational 75 75 % 1 :: Ratio Integer Hugs> toRational 1.5 3 % 2 :: Ratio Integer Hugs> 3%4 + 2%3 ERROR - Undefined variable "%" A classe RationaI no est disponvel no mdulo Prelude e precisa ser importada ou carregada da biblioteca (package/haskell98/ratio.hs). No ltimo exemplo acima a tentativa de operar dois valores do tipo RationaI produz uma mensagem de erro, tendo em vista que a biblioteca no havia sido carregada. 5.7.6 FractionaI A classe FractionaI descende diretamente da classe Num e introduz a diviso, a funo inversa e uma converso de valores do tipo Rational para Fractional. funes assinatura descrio informaI (/) a -> a -> a recip a -> a recip x = 1 / x fromRational Rational -> a ExempIos: Hugs> recip 5 0.2 :: Double Hugs> toRational 2.5 5 % 2 :: Ratio Integer Hugs> fromRational (5 % 2) ERROR - Undefined variable "%" Hugs> :l "packages/haskell98/ratio.hs" Ratio> fromRational(3 % 4) 0.75 :: Double
48 Nos trs ltimos exemplos podemos observar: a) a tentativa de usar a funo fromRational provoca um erro tendo em vista que a biblioteca no estava disponvel; b) a carga da biblioteca e c) o uso de fromRational. 5.7.7 IntegraI A classe IntegraI possui uma herana mltipla, descendendo das classes ReaI e Enum. As funes introduzidas por esta classe so apresentadas a seguir. funes assinatura descrio informaI quot, rem, div, mod a -> a -> a quotRem, divMod a -> a -> (a,a) toInteger a -> Integer ExempIos: Hugs> mod (-5) (3) 1 :: Integer Hugs> rem (-5) (3) -2 :: Integer Hugs> divMod (-5) (3) (-2,1) :: (Integer,Integer) Hugs> quotRem (-5) (3) (-1,-2) :: (Integer,Integer) 5.7.9 ReaIFrac A classe ReaIFrac descende diretamente das classes ReaI e FractionaI introduzindo funes class (Real a, Fractional a) => RealFrac a where properFraction :: (Integral b) => a -> (b,a) truncate, round :: (Integral b) => a -> b ceiling, floor :: (Integral b) => a -> b 5.7.10 FIoating A classe FIoating descende diretamente da classe FractionaI introduzindo funes importantes entre as quais as de exponenciao, de logaritmo, raiz quadrada e as trigonomtricas.
funes assinatura descrio informaI pi a exp, log, sqrt a -> a (**), logBase a -> a -> a sin, cos, tan a -> a asin, acos, atan a -> a 49 sinh, cosh, tanh a -> a asinh, acosh, atanh a -> a Ao definir novas funes, o fazemos tomando por base os tipos de dados existentes na linguagem. O corpo dessas definies tem por base a composio das operaes fornecidas por estes tipos bsicos. Exerccios: 1. Qual o tipo resultante da avaliao da definio da funo mediaC x y = div (x + y) 2? 2. O que ocorre na avaliao de uma expresso em Haskell que usa a definio da funo mediaD x y = div (x + y) 2.0 ? 50 6. EXPRESSES LGICAS E O TIPO BOOLEAN 6.1. INTRODUO Uma caracterstica fundamental dos agentes racionais a capacidade de tomar decises adequadas considerando as condies apresentadas pelo contexto onde est imerso. Uma mquina que sabe apenas fazer contas, ou seja, manusear as operaes aritmticas, ter sua utilidade fortemente reduzida e, portanto no despertar tantos interesses prticos. Os computadores que temos hoje so passveis de serem programados para tomada de deciso, ou seja, possvel escolher entre duas ou mais aes, aquela que se deseja aplicar em um determinado instante. Em nosso paradigma de programao precisamos de dois elementos fundamentais: um tipo de dados para representar a satisfao ou no de uma condio e um mecanismo que use essas condies na escolha de uma definio. Neste captulo discutiremos a natureza das proposies lgicas, sua aplicabilidade na resoluo de problemas e introduziremos um novo tipo de dados, denominado booIean. Satisfazendo logo de sada a curiosidade do leitor lembramos que o nome uma homenagem a George Boole que estudou e formalizou as operaes com estes tipos de valores. 6.2. PROPOSIES LGICAS Revirando o ba das coisas estudadas no ensino fundamental, por certo encontraremos as sentenas matemticas. Lembraremos ento que elas so afirmaes sobre elementos matemticos, tais como os exemplos a seguir: 1. vinte e dois maior que cinco 2. dois mais trs igual a oito 3. todo nmero primo impar O conceito de proposio lgica mais geral, aplicando-se s mais diversas situaes do cotidiano, como nos exemplo a seguir: 1. Maria namorada de Pedro 2. Jos apaixonado por Maria 3. Hoje domingo Analisando o significado da informao contida em uma proposio lgica, podemos concluir que ela ser uma afirmao verdica quando se referir a fatos que realmente acontecem em um determinado mundo. Quando isso no ocorre, conclumos que elas so falsas. Para podermos avaliar uma dada proposio, necessrio ter acesso ao mundo considerado. Sentenas Fechadas: as sentenas 1 a 6 possuem uma caracterstica importante: todos os seus componentes esto devidamente explicitados. Denominamos estas sentenas de sentenas fechadas. Uma sentena fechada pode ser avaliada imediatamente, conferindo o que elas afirmam com o mundo sobre o qual elas se referem. Sentenas Abertas: so outro tipo de proposio importante. Nestas, alguns personagens no esto devidamente explicitados e, portanto a sentena no pode ser avaliada. Quando 51 tratamos sentenas abertas, antes necessrio instanci-las para algum valor. Por exemplo, a sentena matemtica x + 5 ' %- nem sempre verdadeira. Depende do valor que atribuirmos varivel x. Quando atribumos valores s variveis de uma sentena dizemos que estamos instanciando a sentena. No caso acima, podemos instanciar a sentena para os valores 3 e 10, entre outros, obtendo as seguintes instncias: 3 + 5 > 10 10 + 5 > 10 Agora podemos avali-las e concluir que a segunda verdica e a primeira no. Sentenas Compostas: O discurso do cotidiano e at o discurso matemtico podem ser escritos como uma lista de proposies simples. Exemplos: 1. Hoje domingo 2. Aos domingos tem futebol 3. Quando meu time joga, eu assisto Nem sempre isto desejvel ou suficiente. Para resolver a questo, podemos contar com as sentenas compostas. Como por exemplo: a) Gr;s 4 menor que cinco e o quatro tamb4m9 b) Fomingo irei ao futebol ou escre7erei notas de aula9 c) >speranto no 4 uma linguagem de programao Observamos ento, o surgimento de trs palavras para criar essas sentenas e que essas palavras ( e, ou, n4o) no se referem a coisas, propriedades ou fenmenos de um determinado universo. Elas so denominadas de palavras lgicas, visto que a sua funo na linguagem possibilitar a articulao entre as proposies do discurso. A composio de uma proposio pode envolver vrias palavras lgicas, como nos exemplos a seguir: 1. =ois !enor que tr@s e !aior que u! !as n4o !aior que a so!a de tr@s co! doisA 2. Maria ,osta de Bedro e de Coana !as n4o ,osta de 2ntonio# AvaIiao de Sentenas Compostas: Para avaliar sentenas simples, vimos que bastava inspecionar o mundo ao qual ela se refere e verificar se a situao expressa pela sentena ocorre ou no. E como proceder para as sentenas compostas? Um caminho que parece confivel apoiar essa avaliao no papel representado por cada uma das palavras lgicas. Assim, ao considerarmos a proposio Doje fui ao cine!a e ao teatro, 52 s poderemos dizer que ela verdica se tanto a proposio Doje fui ao cine!a quanto a proposio Doje fui ao teatro forem avaliadas como verdicas. Para a proposio composta Maria foi E !issa ou ao cine!a1 devemos consider-la como verdica se uma das duas proposies: %# Maria foi E !issa 2. Maria foi ao cine!a forem avaliadas como verdica. E se as duas forem avaliadas como verdicas? No discurso cotidiano tendemos a pensar nesta situao como inverdica visto que queramos uma ou outra. A linguagem natural no estabelece se devemos ou no explicitar que no estamos falando do caso em que ambas podem ser constatadas. Podemos assumir, portanto, que a nossa sentena composta usando ou ser avaliada como verdica quando pelo menos uma das duas proposies que a compe for avaliada com verdica. No caso da sentena composta usando a palavra lgica n4o, como em Doje n4o c7o$eu diremos que ela ser avaliada como verdica quando a proposio Doje c7o$eu no puder ser constatada e como inverdica no caso oposto. 6.3. O TIPO DE DADOS BOOLEAN Podemos agora discutir sobre a natureza do valor resultante da avaliao de uma sentena. A avaliao de natureza funcional, ou seja, dada uma sentena s ela ser mapeada em um de dois valores distintos. Ento, o contradomnio de uma avaliao um conjunto com apenas dois valores, um para associar com avaliaes verdicas e outras com as inverdicas. Podemos escolher um nome para esses valores. Historicamente, as linguagens de programao os tem denominado de Frue e ;alse. O primeiro para associar com as proposies que podem ser constatadas no mundo considerado e a segunda com as no constatveis. Para a avaliao das proposies compostas, podemos considerar uma funo matemtica cujo domnio e contradomnio so definidos como o conjunto que acabamos de definir com as constantes Frue e ;alse. Assim aval :: <sentena> {True, False} As proposies compostas podem ser formadas pelas operaes lgicas de conjuno, disjuno ou negao. Em Haskell, essas operaes so representadas por: Operao Igica Operador Igico (HaskeII) e && ou || no not 53 Vamos ento formalizar a avaliao de sentenas compostas. Sejam s1 e s2 duas proposies lgicas: 1. O valor lgico da sentena s% GG s2 Frue se e somente se o valor lgico de s% Frue e o valor lgico de s2 Frue e ;alse em caso contrrio; 2. O valor lgico da sentena s% HH s2 ;alse se e somente se o valor lgico de s% ;alse e o valor lgico de s2 ;alse e Frue em caso contrrio: 3. O valor lgico da sentena not s% Frue se e somente se o valor lgico de s% ;alse e ;alse em caso contrrio. TabeIa Verdade: Estas definies tambm podem ser representadas atravs de uma enumerao de suas associaes, formando o que se costuma chamar de Fa+elas Ierdade as quais apresentamos a seguir. s1 s2 s1 && s2 True True True True False False False True False False False False s1 s2 s1 || s2 True True True True False True False True True False False False s1 not s1 True False False True 6.4. OPERADORES RELACIONAIS Quando falamos de proposies lgicas no discurso matemtico, ou melhor, em sentenas matemticas, usamos termos tais como: "menor do que, "menor ou igual, "diferente, entre outros. Estes termos so fundamentais para a nossa inteno de prover os computadores com a capacidade de deciso. Denominamos estes elementos de operadores reIacionais, pois estabelecem uma relao de comparao entre valores de um mesmo domnio. O contradomnio deles do tipo Boolean. A tabela a seguir apresenta esses operadores, suas representaes no Haskell, seus significados e exemplos de uso. operador si,nificado exe!plo resultado == igualdade (2 + 3) == (8 3) True /= Diferena 5 /= (4 * 2 -3) False < Menor (2 + 3) < 6 True <= Menor ou igual (2 * 3) <= 6 True > Maior (4 + 2) > (2 * 3) False >= Maior ou igual (8 3 * 2) >= (15 div 3) False Podemos usar estes operadores para construir novas definies ou simplesmente, em uma sesso de Hugs na avaliao de uma expresso, como apresentado nos exemplos a seguir. 54
6.5. EXPRESSES E DEFINIES Agora que temos um novo tipo de dados, podemos utiliz-lo a nosso servio, escrevendo expresses de forma to natural quanto aquela que usamos para escrever expresses aritmticas. Usando essas expresses podemos ento construir definies cujo tipo resultante seja booleano. Os ingredientes bsicos para construir essas expresses so os operadores relacionais. Expresses simpIes: Por exemplo, para construir uma definio que avalie se um nmero par, podemos usar a seguinte definio:
par x = (mod x 2) == 0 Vejamos alguns exemplos de avaliao da funo par:
> par 5 False > par 148 True > par 0 True > par (truncate ((5 + 2) / 2)) False Outros exemplos de definies: 55 Verificar se a mltiplo de + multiplo a b = (mod a b) == 0 Verificar se a divisor de + divisor a b = multiplo b a Verificar se uma distncia d igual diagonal de um quadrado de lado a diag d a = (a * sqrt 2.0) == d Verificar se um nmero um quadrado perfeito quadp n = (sqrt n)^2 == n Verificar se dois nmeros a e + so termos consecutivos de uma P.A. de razo r spa a b r = (a + r) == b Expresses compostas: Podemos usar agora os operadores lgicos para construir expresses compostas. Veja os exemplos a seguir: Verificar se 3 nmeros esto em ordem decrescente ordc a b c = (a > b) && (b > c) Verificar se um nmero x est no intervalo fechado definido por a e + pert x a b = (x >= a) && (x <= b) ou pert x a b = not ((x < a) || (x > b)) Verificar se um determinado ponto do espao cartesiano est no primeiro quadrante pquad x y = (x > 0) && (y > 0) Verificar se 3 nmeros a, + e c, so lados de um tringulo retngulo tret a b c = ((a^2 + b^2) = = c^2) || ((a^2 + c^2) = = b^2) || ((b^2 + c^2) = =a^2) Quando nossas expresses possuem mais de um operador lgico devemos observar a precedncia de um sobre o outro. Por exemplo, na expresso P || Q && R as letras P, Q e R so expresses lgicas. O operador && ser avaliado primeiro pois tem precedncia sobre o ||, portanto a expresso ser avaliada para True se P for avaliado para True ou se a subexpresso (Q && R) for avaliada para True. 56 Podemos modificar esta ordem utilizando parntesis como nas expresses aritmticas. A expresso acima poderia ser reescrita como (P || Q) && R Agora, para que ela seja avaliada para verdadeira preciso que R seja avaliada como verdadeira. Vejamos mais alguns exemplos de definio de funo booleana:
Verificar se x est fora do intervalo definido por a e b e a e b esto em ordem no decrescente f x a b = ((x <=a) || (x >= b)) && (a <= b) Verificar se x menor que a ou se x maior que b e a e b esto em ordem no decrescente g x a b = (x <=a) || (x >= b) && (a <= b) 6.6. RESOLVENDO UM PROBLEMA: Desejamos verificar se um determinado ponto do espao cartesiano est dentro ou fora de um retngulo paralelo aos eixos, conhecidos o ponto, o canto superior esquerdo e o canto inferior direito do retngulo. Etapa 1 [Entendendo o problema] O ponto pode estar em qualquer quadrante? E o retngulo, pode estar em qualquer quadrante? Basta ter os dois cantos para definir o retngulo? Em que ordem sero informados os cantos? Como se descreve um canto? Um ponto que esteja sobre um dos lados, est dentro ou fora do retngulo? Vamos assumir ento as seguintes decises: discutiremos inicialmente apenas sobre pontos e retngulos localizados no primeiro quadrante. A ordem em que os dados sero informados ser: primeiro o ponto, depois o canto superior esquerdo e por ltimo o canto inferior direito. Para cada ponto sero informadas as duas coordenadas, primeiro a abscissa e depois a ordenada. Os pontos na borda so considerados pertencentes ao retngulo. Etapa 2 [Planejando a Soluo]: Conheo algum problema parecido? Posso decompor este problema em problemas mais simples? Sei resolver um problema mais geral em que este um caso particular? Bom, j nos envolvemos com o problema para verificar se um ponto estava num intervalo linear, este tambm se refere a intervalo. Verificar se um ponto pertence a uma regio qualquer do espao n-dimensional mais geral, se eu conhecesse uma definio para este problema geral, bastaria instanci-la. Ser que posso decompor o problema na verificao de dois espaos lineares, um definido pelos lados paralelos ao eixo das ordenadas e outro paralelo ao eixo das abscissas? Se afirmativo, como combino as duas solues? Para que um ponto esteja dentro do retngulo necessrio que sua ordenada esteja entre as ordenadas dos dois cantos. Sabemos tambm que a abscissa precisa estar entre 57 as abscissas dos dois cantos. sso basta? Para combinar as solues percebemos que suficiente que as duas primeiras estejam satisfeitas. Etapa 3 [Construindo a Soluo] Construindo a soluo - Anteriormente j elaboramos a definio de pertinncia a um intervalo linear, vamos us-la aqui. pert x a b = (x>=a) && (x <= b) pertinncia linear pertsup x y x1 y1 x2 y2 = (pert x x1 x2) && (pert y y2 y1) pertinncia no plano E o teste, para que valores interessam testar? Etapa 4 [Analisando a Soluo]: Existem outras maneiras de resolver o problema? Esta soluo se aplica as outras dimenses? Exerccios 1. Avalie as expresses abaixo, explicando como foram reduzidas para o valor final, que booleano. Se houver erro de avaliao, identifique a causa do erro. a) 3 < 4 || 5 == 7 b) not 3 < 7 c) 3+4 <= 8 && 10 /= 34.7 d) not (3 == 3) || not (4 /= 5) || 7 < 15 && 7 > 2 2. Dado um ponto P(x,y) do plano cartesiano, defina funes que descrevam a sua pertinncia nas regies cinzas das figuras abaixo: i) A regio R1 (regio cinza), do retngulo dado pelas coordenadas do canto superior esquerdo e do canto inferior direito, como mostrado na figura abaixo: ii) A regio R2 do losango (regio cinza), sendo dados os pontos E e D do retngulo e sabendo-se que o crculo tangente aos lados do losango. 58 7. DEFINIES CONDICIONAIS 7.1. INTRODUO Sabemos de nosso conhecimento matemtico que algumas funes no so contnuas em um domnio e que, portanto, possuem vrias definies. Em muitos casos, o domnio D de uma funo est dividido em regies disjuntas que se complementam e, para cada uma dessas regies, existe uma expresso que define o seu mapeamento no contra-domnio. Podemos representar esta situao pela figura abaixo:
ExempIo 1 - Considere a funo que determina o valor da passagem area de um adulto, para um determinado trecho, por exemplo, Vitria-Manaus, considerando a sua idade. Pessoas com idade a partir de 60 anos possuem um desconto de 40% do valor. Considere ainda que a passagem para o trecho considerado custe R$ 600,00. Temos aqui duas formas de calcular o valor da passagem de uma pessoa, dividindo o domnio em dois subconjuntos. O subconjunto dos adultos com menos de 60 anos e o subconjunto dos demais. Podemos definir as duas funes a seguir: vpass1 = 600 vpass2 = vpass1 * 0.6 Para usar uma das definies, temos que explicitamente escolher a que se aplica ao nosso caso. ExempIo 2 - Considere a funo que associa com um determinado rendimento o mposto de Renda a ser pago. At um determinado valor, o contribuinte no paga imposto, e a partir de ento o rendimento dividido em faixas (intervalos), aos quais se aplicam diferentes taxas. Suponha a tabela hipottica abaixo.
59 Faixa aIquota Desconto inferior ou igual a 10.800 0 0 entre 10.801 e 20.000 10 1000 entre 20.001 e 30.000 20 1500 acima de 30.000 25 1800 Para descrever as vrias definies e os correspondentes subdomnios, poderamos escrever separadamente cada definio, construindo, portanto vrias funes, e deixar que o usurio escolha qual usar. Claro que isto traria muitos inconvenientes pois estaramos deixando uma escolha mecnica na mo do usurio, que alm de sobrecarreg-lo com tarefas desnecessrias, ainda estaria expondo-o ao erro por desateno. Mas vamos l construir as funes independentes. ir1 s = 0 ir2 s = s * 0.1 - 1000 ir3 s = s * 0.2 - 1500 ir4 s = s * 0.25 - 1800 Agora, para us-las, o usurio pega o seu salrio, olha a tabela e seleciona qual funo aplicar. A escolha de qual definio usar para uma dada situao em si, um tipo de computao. Podemos descrever essa computao com expresses condicionais, vamos deixar que o computador escolha. Descrevemos cada subdomnio com a respectiva funo aplicvel e deixemos que ele escolha a definio a aplicar, dependendo do valor fornecido. Vejamos ento como isso pode ser feito nas sees subseqentes. 7.2. A ESTRUTURA IF-THEN-ELSE Uma expresso condicional construda com if*t7en*else possui a seguinte sintaxe: if <expresso lgica> then <expresso 1> eIse <expresso 2> onde:
<expresso lgica> Uma expresso descrevendo uma condio a ser satisfeita, envolvendo operadores relacionais e operadores lgicos. 60 <expresso1> e <expresso2> 1. Expresses descrevendo um valor a ser produzido como resposta entrada fornecida e, como a expresso total tem que ser de um nico tipo, as duas expresses devem ser do mesmo tipo. 2. Cada uma destas expresses pode ser inclusive outra condicional, dentro da qual pode haver outras e assim sucessivamente. 3. Quando a <expresso lgica> avaliada para Frue o valor resultante ser o que for obtido pela avaliao da <expresso 1> caso contrrio ser o obtido pela avaliao da <expresso 2>
Para a funo que calcula o valor da passagem area podemos ento construir a seguinte definio: vpass x = if x < 60 then 600 else 360 rvore de deciso: Podemos representar as expresses condicionais atravs de uma notao grfica denominada de rvore de deciso. importante considerar que este tipo de representao uma ferramenta importantssima para estruturarmos a soluo de problemas que requerem expresses condicionais.
ExempIo 3 Definir a funo que determina o valor absoluto de um nmero. Sabemos que esta funo se define em dois subdomnios: subdomnio expresso x < 0 - x x >= 0 x Como s temos duas possibilidades, podemos codificar da seguinte maneira: absoluto x = if x < 0 then -x eIse x 61 Para concluir esta apresentao voltemos ao nosso exemplo 2 que define a funo para clculo do mposto de Renda. O domnio neste caso deve ser quebrado em quatro subdomnios e para cada um deles construiremos uma expresso. domnio funo s > 10800 ir1 s pert s 10801 20000 ir2 s pert s 20001 30000 ir3 s s > 30000 ir4 s Para a dodificao, precisaremos quebrar sucessivamente o domnio da funo em intervalos. A determinao dos intervalos realizada da seguinte maneira: primeiro dividimos o domnio entre o primeiro intervalo e o restante, que por sua vez, dividido entre o segundo intervalo e o seu restante e assim sucessivamente. A codificao final pode ser: ir s = if s <= 10800 then ir1 else if pert s 10800 20000 then ir2 else if pert s 20001 30000 then ir3 else ir4 here ir1 = 0 ir2 = s * 0.1 - 1000 ir3 = s * 0.2 - 1500 ir4 = s * 0.25 - 1800 pert x a b = x>=a && x<=b 7.2.1 USANDO O IF-THEN-ELSE EXEMPLO 1: Considere um mapeamento de valores numricos onde o domnio se divide em 4 regies, cada uma das quais possui diferentes formas de mapeamento. As regies so apresentadas na figura abaixo, numeradas da esquerda para direita. Observe ainda que as extremidades so abertas. Considere ainda a seguinte tabela de associaes:
regio mapeamento desejado regio 1 o dobro de x 62 regio 2 o sucessor de x regio 3 o quadrado de x regio 4 o simtrico do quadrado de x Podemos analisar as regies atravs do seguinte diagrama: O que nos levar seguinte definio: f x = if x < 0 then if x < (-15) then 2 * x else x + 1 else if x < 10 then x ^2 else - (x ^ 2) Exemplo 2: Dados trs nmeros inteiros distintos, determinar o maior deles. Podemos explorar uma soluo da seguinte maneira. Considere um retngulo e divida-o horizontalmente em 2 partes, a parte de cima representa as situaes onde a > b e a de baixo aquelas onde b > a. Divida agora o retngulo verticalmente, em cada uma das regies anteriores surgiro 2 metades. Na de cima, representamos agora a relao entre a e c. Na de baixo, a relao entre b e c. x < 0 X < -15 X > 10 True True True FaIse FaIse FaIse 63 Explorando as relaes entre os nmeros Representando as relaes atravs de uma rvore de deciso Traduzindo a rvore de deciso para Hugs, chegamos seguinte definio: maior a b c = if a > b then if a > c then a else c else if b > c then b else c 7.3 DEFINIES PROTEGIDAS (guarded commands): A estrutura IF-THEN-ELSE foi apresentada por primeiro por questes histricas, por tratar-se de uma forma pioneira de escrever definies condicionais. Entretanto, algumas vezes podemos lanar mo de estruturas mais simples e mais legveis. As definies protegidas, tambm conhecidas por "guarded commands permitem que se escreva para uma mesma funo, vrias definies, cada uma delas protegida por uma expresso lgica. <nome da funo> <parmetros> | <proteo 1> = <definio 1> | <proteo 2> = <definio 2> | <proteo 3> = <definio 3> . . . | <proteo n> = <definio n> [ | otherwise = <definio n + 1> ] A ltima clausula da definio opcional, por este motivo est apresentada dentro de colchetes. Vejamos como podemos reescrever a definio da nossa funo "ir para clculo do imposto de renda. 64 ir' s | s<=10800 = ir1 | pert s 10800 20000 = ir2 | pert s 20001 30000 = ir3 |otherwise = ir4 where ir1 = 0 ir2 = s * 0.1 - 1000 ir3 = s * 0.2 - 1500 ir4 = s * 0.25 - 1800 Exerccios 1. Reescreva, usando definies protegidas. A definio da funo que determina o maior de 3 nmeros inteiros fornecidos. 2. Sejam C(x 1 ,y 1 ) o centro da circunferncia de raio r e tambm do quadrado de lados paralelos aos eixos cartesianos e inscrito na circunferncia. Escreva uma funo que descreva a regio de pertinncia do ponto P na figura abaixo, dados C(x 1 ,y 1 ) , r e o ponto P (x,y), 65 7 1 1 2 2 3 4 5 6 8. O TESTE DE PROGRAMAS 8.1. INTRODUO: No basta desenvolver um programa para resolver um dado problema. preciso garantir que a soluo esteja correta. Muitos erros podem ocorrer durante o desenvolvimento de um programa e, portanto temos que garantir que o programa que ir ser executado est livre de todos eles. Ao conceber a soluo podemos nos equivocar e escolher caminhos errados. Precisamos eliminar esses equvocos. Ao codificarmos a nossa soluo podemos cometer outros erros ao no traduzirmos corretamente nossa inteno. Esses erros podem ocorrer por um mau entendimento dos elementos da linguagem ou at mesmo por descuido, o certo que eles ocorrem. Uma estratgia muito til, mas no infalvel, o teste de programa. Em sua essncia, o teste de programa consiste em submeter um programa ao exerccio de algumas instncias do problema e comparar os resultados esperados com os resultados obtidos. 8.2. O PROCESSO DE TESTE: Em primeiro lugar devemos escolher as instncias apropriadas, no basta escolh-las aleatoriamente. A seguir devemos determinar, sem o uso do programa, qual o valor que deveria resultar quando o programa for alimentado com essas instncias. O passo seguinte consiste em submeter cada instncia ao programa e anotar o resultado produzido por ele. Finalmente devemos comparar cada valor esperado com o valor produzido e descrever qual o tipo de ocorrncia. Um exempIo: Considere o problema de identificar se um dado ponto est ou no localizado no primeiro quadrante do espao cartesiano. Considere ainda a seguinte definio:
primquad x y = (x >= 0) && (y >= 0) Precisamos agora verificar se ela atende nossa inteno. Para tanto devemos escolher algumas instncias, prever o resultado esperado e em seguida submeter ao HUGS, para ver o que acontece. Que pares de valores deveremos escolher? Bom, vamos escolher uns pares usando a seguinte estratgia: um par onde x maior que y, outro onde y seja maior que x e um terceiro em que os dois sejam iguais. Gerando uma planilha como apresentada a seguir.
x y resuItado esperado resuItado obtido diagnstico -5 -2 False -2 -5 False 5 5 True Podemos agora submeter as instncias avaliao do sistema, obtendo a seguinte interao: 66 ? primquad (-5) (-2) FaIse ? primquad (-2) (-5) FaIse ? primquad 5 5 True ? Podemos agora completar o preenchimento de nossa planilha, obtendo a tabela a seguir:
x y resuItado esperado resuItado obtido diagnstico -5 -2 False False sucesso -2 -5 False False sucesso 5 5 True True sucesso Verificando as diversas linhas da coluna "Diagnstico, parece que nosso programa est correto. Veja que ele passou com sucesso em todos os testes! Que pena que no seja verdade. Apesar de passar em todos os testes a que foi submetido, ele no funciona corretamente. Tudo que podemos afirmar neste instante que para os valores usados, o programa funciona corretamente. E para os outros valores, ser que funciona corretamente? Outros valores? Quais? 8.3. PLANO DE TESTE: Para escolher os valores que usaremos, antes de mais nada devemos identificar as classes de valores que sero relevantes para o teste, em um segundo instante podemos ento escolher os representantes destas classes. Quando temos um parmetro, os possveis valores a serem usados so todas as constantes do domnio. Para o caso de um parmetro do tipo int, existem 65536 valores diferentes. Testar nosso programa para todos esses valores implicaria em determinar a mesma quantidade de resultados esperados e em seguida digitar e submeter esta mesma quantidade de instncias do problema para o sistema e ainda depois conferir um a um os resultado obtidos com os esperados. Enfim, um trabalho imenso. magine agora se fossem dois parmetros? A quantidade de pares seria da ordem de 4.29497 x 10^09 (algo da ordem de quatro bilhes). E para 3 parmetros? E para n parmetros? Nmeros cada vez mais enormes. Por este caminho, testar programas seria invivel. Felizmente no precisamos de todos eles, basta identificar as classes distintas que importam para o problema, ou seja, as classes de equivalncia relevantes. sto pode ser obtido analisando o enunciado estendido do problema. No caso de nosso exemplo anterior, analisando melhor a definio, podemos identificar que por definio, o espao cartesiano se divide em quatro regies. A primeira, onde ambos as coordenadas so positivas, denominamos de primeiro quadrante. A segunda, onde a coordenada x negativa e a y positiva, denominamos de segundo quadrante. O terceiro quadrante corresponde ao espao onde ambas as coordenadas so 67 negativas. Ainda temos o quarto quadrante onde a coordenada x positiva e a y negativa. E os pontos que ficam em um dos eixos ou na interseo destes, qual a classificao que eles tm? Bom, podemos convencionar que no esto em nenhum dos 4 quadrantes descritos. Resumindo, nosso plano de teste deve contemplar estas situaes, conforme ilustrado na tabela a seguir. x y quadrante positivo positivo primeiro negativo positivo segundo negativo negativo terceiro negativo positivo quarto nulo qualquer no nulo eixo das ordenadas qualquer no nulo nulo eixo das abscessos nulo nulo origem
bom observar ainda que este plano pode e deve ser preparado antes mesmo de elaborar o programa, para faz-lo, precisamos apenas da especificao detalhada. Alm disso, este plano no precisa ser feito pelo programador responsvel pela elaborao da soluo, qualquer outro programador, de posse do enunciado detalhado, pode se encarregar da tarefa. Este tipo de plano serve para alimentar o teste denominado de caixa preta. Esta denominao se deve ao fato de no ser necessrio conhecermos a estrutura da soluo para elaborar o plano. Outro aspecto positivo da elaborao do plano o mais cedo possvel que contribu para um melhor entendimento do problema. Voltando ao nosso exemplo, podemos agora elaborar a nossa planilha de teste considerando as classes de equivalncia a serem definidas. Uma questo que surge como escolhemos o representante de uma classe? Existem melhores e piores? No nosso caso, como pode ser qualquer valor positivo ou negativo, podemos escolher valores de um dgito apenas, no mnimo escreveremos e digitaremos menos. x y resuItado esperado resuItado obtido diagnstico 2 3 True -2 3 False -2 -3 False 2 -3 False 0 3 False 0 -3 False 2 0 False -2 0 False 0 0 False 68 8.4. REALIZANDO O TESTE: Vejamos agora o resultado de nossa interao com o HUGS. ? primquad 2 3 True ? primquad (-2) 3 FaIse ? primquad (-2) (-3) FaIse ? primquad 2 (-3) FaIse primquad 0 (-3) FaIse ? primquad 0 3 True ? primquad 0 (-3) FaIse ? primquad 2 0 True ? primquad (-2) 0 FaIse ? primquad 0 0 True ?
Voltemos agora para nossa planilha e vamos preench-la na coluna de resultado obtido e diagnstico. x y resuItado esperado resuItado obtido diagnstico 2 3 True True sucesso -2 3 False False sucesso -2 -3 False False sucesso 2 -3 False False sucesso 0 3 False True falha 0 -3 False False sucesso 2 0 False True falha -2 0 False False sucesso 0 0 False True falha 8.5. Depurao: Uma vez testado o programa e identificado que ocorreram instncias para as quais a nossa definio no est fazendo a associao correta, ou seja, o valor obtido diferente do esperado, devemos passar a uma nova fase. Depurar um programa um processo que consiste em buscar uma explicao para os motivos da falha e posteriormente corrigi-la. Obviamente isto tambm no um processo determinante e nem possumos uma frmula mgica. Ao longo de nossa formao de programadores iremos 69 aos poucos incorporando heursticas que facilitem esta tarefa. Por enquanto muito cedo para falarmos mais do assunto e vamos concluir com um fechamento do problema anterior. Aps concluir as modificaes devemos testar o programa novamente. Depurando nossa soIuo - Podemos concluir por simples inspeo da nossa ltima planilha (aquela com todas as colunas preenchidas) que nossa soluo est incorreta. Uma interpretao dos resultados nos leva hiptese de que a nossa soluo considera que quando o ponto se localiza na origem ou em um dos eixos positivos, a nossa definio est considerando que eles esto no primeiro quadrante. Passo seguinte, verificar se de fato nossa definio incorre neste erro. Em caso afirmativo, corrigi-la e a seguir, resubmet-la aos testes. Observando a nossa definio inicial, podemos concluir que de fato nossa hiptese se confirma. primquad x y = (x >= 0) && (y >= 0)
Podemos ento modific-la para obter uma nova definio, que esperamos que esteja correta. primquad x y = (x > 0) && (y > 0)
Agora submet-la novamente ao teste e ver o que acontece! 8.6. UMA SNTESE DO PROCESSO: O processo , como vimos, repetitivo e s se encerra quando no identificarmos mais erros. O diagrama ilustra o processo como um todo. Bas lembre-se1 isto ainda no garante que seu programa este@a 1!!H corretoI 2uando no identificamos erro1 apenas podemos concluir que para as instJncias que usamos o nosso programa apresenta os resultados esperados
70 Exerccios: 1. Dado um ponto P(x,y) do plano cartesiano, defina funes que descrevam a sua pertinncia na regio cinza da figura abaixo (a regio R1 - regio cinza - do retngulo dado pelas coordenadas do canto superior esquerdo e do canto inferior direito. Faa o plano de teste para o seu programa: 71 E D 9. RESOLVENDO PROBLEMAS - OS MOVIMENTOS DO CAVALO 9.1. INTRODUO: Considere o jogo de xadrez, onde peas so movimentadas em um tabuleiro dividido em 8 linhas e oito colunas. Considere ainda os movimentos do cavalo, a partir de uma dada posio, conforme diagrama a seguir, onde cada possvel movimento designado por !i. No esquema, o cavalo localizado na posio (5, 4) pode fazer oito movimentos, onde o primeiro deles, !%, levaria o cavalo para a posio (7,5). 8 m3 m3 m3 m3 m3 m3 m3 M3 7 6 m3 m2 5 m4 m1 4 C 3 m5 m8 2 m6 m7 1 1 2 3 4 5 6 7 8 9.2. PROBLEMA 1: Escreva uma funo que determina se, a partir de uma dada posio, o cavalo pode ou no realizar o primeiro movimento. Vamos cham-la de p!o$, e denominar seus parmetros (a posio corrente), de x e . Eis alguns exemplos de uso de nossa funo e os valores esperados: instncia resuItado pmov 5 4 True pmov 8 1 False pmov 1 1 True pmov 1 8 False 9.2.1. Soluo - A soluo pode ser encontrada atravs da construo de uma expresso booleana que avalie se a nova posio, ou seja, aquela em que o cavalo seria posicionado pelo primeiro movimento, est dentro do tabuleiro. Como o cavalo, no primeiro movimento, anda duas casas para direita e uma para cima, basta verificar se as coordenadas da nova posio no ultrapassam a oitava fileira (linha ou coluna). Codificando em HUGS, temos ento: pmov x y = (x + 2 <= 8 ) && (y + 1 <= 8)
9.2.2. TESTANDO A SOLUO - Como j discutimos anteriormente, para verificar a correo de nossa soluo, devemos submet-la a um teste. Para tanto devemos escolher as classes de equivalncias relevantes e, a partir delas, produzir a nossa planilha de teste. Olhando para a especificao do problema, podemos identificar 4 conjuntos de 72 valores que se equivalem para os fins do nosso programa, como podemos observar na figura abaixo: 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9.2.3. Estendendo o Problema - Podemos fazer o mesmo para todos os demais movimentos, obtendo com isso as seguintes definies: pmov x y = (x + 2 <= 8 ) && (y + 1 <= 8) smov x y = (x + 1 <= 8 ) && (y + 2 <= 8) tmov x y = (x - 1 >= 1 ) && (y + 2 <= 8) qmov x y = (x - 2 >= 1 ) && (y + 1 <= 8) qtmov x y = (x - 2 >=1 ) && (y - 1 >= 1) sxmov x y = (x - 1 >= 1 ) && (y - 2 >= 1) stmov x y = (x + 1 <= 8 ) && (y - 2 >= 1) omov x y = (x + 2 <= 8 ) && (y - 1 >= 1) 9.2.4. dentificando Abstraes - Podemos agora indagar, olhando para as oito definies, sobre a ocorrncia de algum conceito geral que permeie todas elas. Poderamos tambm ter feito isso antes. Como no o fizemos, faamo-lo agora. Podemos observar que todas elas avaliam duas expresses e que ambas testam fronteiras que podem ser margem direita, margem esquerda, margem superior ou margem inferior. Podemos observar ainda, que o par margem direita e margem superior testam o mesmo valor 8, assim como ocorre com as duas outras, que testam o valor 1. Com isso podemos definir duas novas funes, f e ,, para testar estes limites. Agora, as nossas definies anteriores podem ser reescritas, usando as duas abstraes identificadas. pmov x y = f (x + 2 ) && f( y + 1) smov x y = f (x + 1) && f (y + 2) tmov x y = g (x - 1) && f (y + 2) qmov x y = g (x - 2) && f (y + 1) qtmov x y = g (x - 2) && g (y 1) sxmov x y = g (x - 1) && g (y 2) stmov x y = f (x + 1 ) && g (y 2) omov x y = f (x + 2) && g (y 1) f w = w <= 8 g w = w >= 1 73 9.2.5. Anlise da Soluo - O que ser que ganhamos com esta nova forma de descrever a nossa soluo? Podemos indicar pelo menos trs indcios de vantagem na nova soluo: 1. Clareza - Na medida em que agora est explicitado, que todas as oito funes para verificar os movimentos possuem estrutura semelhante e que todas esto usando funes para verificar a ultrapassagem das bordas; 2. Manuteno - Se nosso tabuleiro mudasse, ou seja, passasse a ter 9 linhas por nove colunas, bastaria alterar a funo f e tudo estaria modificado, ao invs de termos que alterar as oito definies. 3. Reuso - As duas funes que testam as bordas poderiam ser usadas para construir funes para avaliar o movimento de outras peas do jogo de xadrez. 9.3. PROBLEMA 2: Sabemos que para cada posio alguns movimentos podem ser realizados e outros no. Como ordenamos os movimentos no sentido anti-horrio, gostaramos de obter, para uma dada posio, dos movimentos que podem ser realizados, aquele que possui o menor ndice. Vejamos o esquema abaixo.
8 m4 m1 m3 m3 C1 7 C3 m5 6 m5 m8 m6 5 m6 m7 4 3 m2 m3 2 m1 m4 1 C4 C2 1 2 3 4 5 6 7 8 Podemos observar que o cavalo C1 s pode fazer os movimentos m5 e m6 e que o de menor ndice m5. J o cavalo C2 s pode fazer os movimentos m3 e m4 e que o de menor ndice o m3. Enquanto isso o cavalo C3 pode fazer os movimentos m1, m4, m5, m6, m7 e m8. Para este caso o movimento de menor ndice o m1. Vamos chamar esta funo de qual!o$ e, como no problema anterior, os parmetros sero as coordenadas da posio atual do cavalo. Eis alguns exemplos de uso de nossa funo: nstncia resultado qualmov 8 1 3 qualmov 8 8 5 qualmov 3 7 1 qualmov 1 1 1 74 9.3.1. SOLUO - Bem, como j sabemos, para verificar se um dado movimento !i possvel, basta arranjar um meio de sair verificando um-a-um os movimentos, a partir do primeiro (m1) e encontrar o primeiro que pode ser realizado. Quando isso ocorrer podemos fornecer como resposta o seu ndice. Podemos construir para isso uma rvore de deciso. Na raiz da rvore estar a pergunta J poss5$el reali1ar o !o$i!ento !%JK Em caso afirmativo (brao esquerdo da rvore), mapeamos no valor 1 e em caso negativo (brao direito), o que devemos fazer? Bom, a podemos comear uma nova rvore (na verdade uma sub-rvore), cuja raiz ser: J poss5$el reali1ar o !o$i!ento !2JK E da, prosseguimos at que todos os movimentos tenham sido considerados. A rvore resultante ser: 9.3.2. CODFCANDO A SOLUO - Vamos ento explorar os recursos da linguagem para transformar nosso plano em um programa que de fato possa ser "entendido" pelo nosso sistema de programao (HUGS). Como podemos observar temos aqui o caso de uma funo que no contnua para o domnio do problema. Pelo que sabemos at ento, no d para expressar a soluo como uma nica expresso simples. Resta-nos o recurso das expresses condicionais. Para verificar se um dado movimento satisfeito podemos usar as funes que construmos anteriormente e com isso obtemos a seguinte definio: 75 qualmov x y = if pmov x y then 1 eIse if smov x y then 2 eIse if tmov x y then 3 eIse if qmov x y then 4 eIse if qtmov x y then 5 eIse if sxmov x y then 6 eIse if stmov x y then 7 eIse if omov x y then 8 eIse 0 9.3.3. Anlise da Soluo - EM PRMERO LUGAR, NCLUMOS A RESPOSTA GUAL A ZERO (0) QUANDO O MOVMENTO M8, O LTMO A SER AVALADO, RESULTA EM FRACASSO. PARA QUE SERVE SSO? ACONTECE QUE SE A POSO DE ENTRADA NO FOR VLDA, OU SEJA, UMA OU AMBAS AS COORDENADAS NO PERTENCEREM AO NTERVALO [1, 8], NENHUM MOVMENTO SERA VLDO E SE NO PROVDENCARMOS UMA RESPOSTA ALTERNATVA, NOSSA FUNO SERA PARCAL. MAS STO RESOLVE DE FATO NOSSO PROBLEMA? O QUE OCORRERA SE A POSO DE ENTRADA FOSSE (0, 0)? BOM, NOSSA FUNO DETERMNARA QUE O PRMERO MOVMENTO PODERA SER REALZADO E STO NO VERDADE. A NVENO DE UM RESULTADO EXTRA PARA NDCAR QUE NO H SOLUO POSSVEL, TRANSFORMANDO UMA FUNO PARCAL EM UMA FUNO TOTAL, PARECE SER BOA, MAS COMO FO FETA NO RESOLVEU. EM GERAL O MELHOR NESTAS STUAES PRECEDER TODA E QUALQUER TENTATVA DE DETERMNAR A SOLUO ADEQUADA, POR UMA AVALAO DA VALDADE DOS DADOS DE ENTRADA. NESTE CASO, BASTARA VERFCAR SE OS DOS ESTO NO NTERVALO [1, 8]. VAMOS CONSTRUR AQU UMA FUNO QUE AVALA A PERTNNCA DE UM VALOR A UM NTERVALO NUMRCO, CONFORME DEFNO A SEGUR: pert x a b = (x >= a) && (x<=b) Especulando um pouco mais sobre a nossa soluo, podemos observar que o movimento !L, jamais ocorrer! Analisando os possveis movimentos chegaremos concluso de que para nenhuma posio, o oitavo o nico movimento possvel. Sugerimos fortemente que o leitor prove este teorema. Portanto a soluo final pode ser: qualmov x y = if not (pert x 1 8) || not (pert y 1 8) then 0 eIse if pmov x y then 1 eIse if smov x y then 2 eIse if tmov x y then 3 eIse if qmov x y then 4 eIse if qtmov x y then 5 eIse if sxmov x y then 6 eIse 7 76 9.4. REVISITANDO O PROBLEMA 1: Observando a soluo encontrada para o problema 1, constatamos que embora a noo de movimento do cavalo seja nica, quem precisar saber se um dado movimento vlido, precisar conhecer o nome das oito funes. Embora seja cedo para falarmos de interface homem-mquina, j d para dizer que estamos sobrecarregando nosso usurio ao darmos oito nomes para coisas to parecidas. Ser que temos como construir uma s funo para tratar o problema? Vamos reproduzir aqui a interface das oito: pmov x y smov x y tmov x y qmov x y qtmov x y sxmov x y stmov x y omov x y
Propositadamente escrevemos o nome delas com um pedao em vermelho e outro em preto. Seria alguma homenagem algum time que tem essas cores? Na verdade estamos interessados em destacar que a pequena diferena nos nomes sugere que temos uma mesma funo e que existe um parmetro oculto. Que tal explicit-lo? Podemos agora ter uma funo com 3 parmetros, sendo o primeiro deles para indicar o nmero do movimento que nos interessa. A interface agora seria: mov m x y Agora, por exemplo, para solicitar a avaliao do stimo movimento para um cavalo em (3, 4), escrevemos: ? mov 7 3 4 True ? Muito bem, e como codificaremos isso? 9.4.1. SOLUO - Precisamos encampar em nossa soluo o fato de que a nossa funo possui diferentes formas de avaliao, para diferentes valores do domnio, algo parecido com a soluo do problema 2, ou seja, a nossa funo no continua e portanto temos que selecionar qual a definio apropriada para um determinado valor de m. Devemos construir uma rvore de deciso. Aqui deixamos esta tarefa a cargo do leitor e passamos direto codificao conforme apresentamos a seguir: 77 mov m x y = if not (pert m 1 8) || not (pert x 1 8) || not (pert y 1 8) then False else if m == 1 then pmov else if m == ! then smov else if m == 3 then tmov else if m == 4 then qmov else if m == " then qtmov else if m == 6 then sxmov else if m == # then stmov else omov here
pmov = ... smov = ... tmov = ... ... 9.4.2. Anlise da soluo - Ao contrrio da soluo do problema 2, onde necessariamente a validade dos movimentos tinha que ser verificada do primeiro para o ltimo, pois nos interessava saber qual o primeiro possvel de ser realizado, o leitor pode observar que esta ordem aqui no necessria. Tanto faz se perguntamos primeiro se m=7 ou se m=3. Ser que podemos tirar algum proveito disso? Alertamos o iniciante, que devemos sempre identificar propriedades internas do problema e explor-las adequadamente. Qual a influncia desta ordem na eficincia da avaliao de uma expresso submetida ao HUGS? Para responder, basta lembrar que as condies so avaliadas seqencialmente. Por exemplo, se m=8, teremos que avaliar 8 condies, se m=1 faremos 2 avaliaes e se m est fora do domnio faremos uma avaliao. Ou seja, no pior caso faremos 8 avaliaes e no melhor caso uma (1). Em mdia, ao longo do uso da funo, assumindo uma distribuio uniforme dos valores de m, faremos 4 avaliaes. E se o intervalo fosse de 100 elementos distintos, quantas avaliaes de condies faramos em mdia? Ser possvel elaborar solues onde este nmero de avaliaes seja menor? 9.4.3. A resposta sim! J que a ordem das avaliaes no importa, podemos buscar uma ordem mais conveniente para reduzir o nmero de avaliaes por cada instncia avaliada. A idia geral para estes casos obtida a partir de um conceito de vasta utilidade na Computao. Falamos de r$ore +inria e o leitor por certo ouvir falar muito dela ao longo da vida enquanto profissional da rea. O caminho consiste em dividir o intervalo considerado ao meio e fazer a primeira pergunta, por exemplo, m<=4? Dividindo em 2 o intervalo de comparaes, para cada um destes podemos novamente dividir ao meio, at que no mais tenhamos o que dividir, como ilustramos no diagrama a seguir, onde cada linha representa um passo da diviso dos intervalos. <1 1 2 3 4 5 6 7 8 >8 78 <1 1 2 3 4 5 6 7 8 >8 <1 1 2 3 4 5 6 7 8 >8
Que tambm podemos representar por:
A codificao ficar ento: mov m x y = if not(pert m 1 8)||not(pert x 1 8)||not (pert y 1 8) then False else if m <= 4 then if m<= 2 then if m== 1 then pmov else smov else if m==3 then tmov else qmov else if m<= 6 then if m==5 then qtmov else sxmov else if m == 7 then stmov else omov where pmov = ... smov = ... tmov = ... ...
Se fizermos uma anlise das possibilidades veremos que para qualquer valor de m o nmero mximo de avaliaes que faremos ser sempre igual a quatro! E se fosse 100 valores para m, qual seria o nmero mximo de comparaes? E para 1000? E 1000000? 79 O nmero de comparaes, seguindo este esquema, aproximadamente igual ao logaritmo do nmero de valores na base 2. Portanto para 100 teramos 7, para 1000 teramos 10 e para um milho teramos 20. Veja que bem inferior ao que obtemos com o esquema anterior, tambm denominado de linear. Confira a comparao na tabela abaixo. nmero de vaIores esquema Iinear (nmero mdio) esquema binrio (nmero mximo) 8 4 4 100 50 7 1000 500 10 1000000 500000 20 Exerccios: 1. Escreva uma funo que determina se, a partir de uma dada posio e de um movimento vlido (1 a 8), o cavalo pode ou no realizar o movimento desejado. Se puder, sua funo deve responder em qual linha do tabuleiro o novo ponto, obtido aps a realizao do movimento, estar situado. Sua funo deve se chamar movL e uma instncia dela seria movL 1 4 5 5. 2. Faa uma funo similar movL, mas agora sua funo deve se chamar movC e deve responder a coluna correspondente da nova posio do cavalo no tabuleiro, aps a realizao do movimento desejado. 80 10. TUPLAS 10.1. INTRODUO At ento trabalhamos com valores elementares. Ou seja, valores atmicos, indivisveis no que diz respeito as operaes de seu tipo de dados. Por exemplo, Frue, 523, L#23., so valores elementares dos tipos Boolean, nteger e Float, respectivamente. No podemos, por exemplo, nos referir, dentro da linguagem, ao primeiro algarismo do valor 523. Dizemos que esses so tipos primitivos da linguagem. Alm desses trs, a linguagem Haskell prov ainda o tipo c7ar. O elenco de tipos primitivos de uma linguagem pode ser entendido como o alicerce da linguagem para a descrio de valores mais complexos, que so denominados genericamente de $alores estruturados. Os problemas que pretendemos resolver esto em universos mais ricos em abstraes do que esse das linguagens. Para descrever esses mundos, precisamos usar abstraes apropriadas para simplificar nosso discurso. Por exemplo, quando quero saber onde algum mora, eu pergunto: Qual o seu endere3o? Quando quero saber quando um amigo nasceu, eu pergunto: Qual a data do seu nascimento? E quando quero saber para onde algum vai deslocar o cavalo no jogo de xadrez, eu pergunto: Qual a nova posi34o? Os nomes endereo, data e posio designam valores estruturados. Uma data tem trs partes: dia, ms e ano. Um endereo pode ter quatro: rua, nmero, complemento e bairro. J a posio no tabuleiro tem duas partes, linha e coluna. Para possibilitar a descrio de valores dessa natureza, a linguagem Haskell dispe de um construtor denominado tupla. Podemos definir uma tupla como um agregado de dados, que possui quantidade pr-estabelecida de componentes (dois ou mais), e onde cada componente da tupla pode ser de um tipo diferente (primitivo ou no). 10.2. DEFINIO A representao de uma tupla feita com a seguinte sintaxe: (t1, t2, t3, ..., tn) Onde cada ti um termo da tupla. Se Fi o tipo de ti, ento o universo de uma tupla dado por: FF ( F% x F2 x ### x Fn Sabendo-se que os conjuntos bases dos tipos Fi so ordenados, tambm os elementos de FF o sero. 81 Exemplos de tuplas: Inteno Representao 1 uma data (15, 05, 2000) 2 uma posio no tabuleiro de xadrez (3, 8) 3 uma pessoa ("Maria Aparecida", "solteira", (3, 02, 1970)) 4 um ponto no espao (3.0, 5.2, 34.5) 5 uma carta de baralho (7, "espada") 6 validao de dados (True, 3) 7 validao de dados (False, div 5 2) 8 uma tupla formada por expresses (x/=0 && y /= 0, (x > y, if x*y /( 0 t7en if x > y t7en div x y else div y x else 0)) 9 uma tupla formada por expresses (x, y, x + y) 10.3. COMPONDO TUPLAS Os exemplos apresentados j parecem suficientes para que tenhamos entendido como descrever um valor do tipo tupla. Vamos ento apenas comentar sobre os exemplos e ilustrar o uso de tuplas nas definies. Uma tupla um valor composto. sto significa que devemos colocar entre parntesis e separados por vrgulas os componentes deste valor. Cada componente um valor, descrito diretamente ou atravs de expresses envolvendo operadores. Nos exemplos de 1 a 7, todos eles usam constantes. O exemplo 7 apresenta um dos valores descrito por uma expresso. No exemplo 3, um dos termos uma outra tupla. Qualquer termo pode ser uma tupla. Um valor pode tambm ser paramtrico, como no exemplo 9, onde temos uma tupla de 3 termos, todos eles paramtricos, mas o terceiro depende dos dois primeiros. Podemos dizer que esta tupla descreve todas as triplas onde o terceiro termo pode ser obtido pela soma dos dois primeiros. No exemplo 8 podemos observar o uso de expresses condicionais. Observa-se a tambm a descrio de um termo do tipo boolean, atravs de uma expresso relacional combinada com operadores lgicos. Vejamos agora alguns exemplos de uso de tuplas na descrio de valores. ExempIo 1: Desejamos definir uma funo, para que dados 2 valores, seja produzido uma tripla onde os dois primeiros termos so idnticos aos elementos fornecidos, em ordem inversa, e o terceiro termo seja igual soma dos dois.
82 triplaS a b = ( b, a, a + b ) ExempIo 2: Vamos definir uma funo que produza o quociente e o resto da diviso inteira de dois nmeros. divInt a b = ( q, r) where q = div a b r = rem a b ExempIo 3: Voltemos definio das razes de uma equao do 2o. grau. Vimos anteriormente que como eram duas, precisvamos definir tambm duas funes. Agora podemos agrup-las em uma nica definio: re2g a b c = ( x1, x2) where x1 = ((-b) + e)/ duploA x2 = ((-b) - e)/ duploA e = sqrt (b^2 - 4.0*a*c) duploA = 2.0 * a ExempIo 4: Voltemos aos movimentos do cavalo no jogo de xadrez. Vamos definir uma funo que produza a nova posio, usando o primeiro movimento vlido segundo o que se discutiu em Resolvendo Problemas - Os Movimentos do Cavalo.
qPos x y = if f x2 && f y1 then (x2,y1) else if f x1 && f y2 then (x1,y2) else if g x1e && f y2 then (x1e,y2) else if g x2e && f y1 then (x2e,y1) else if g x2e && g y1e then (x2e,y1e) else if g x1e && g y2e then (x1e,y2e) else if f x1 && f y2e then (x1,y2e) else (0,0) where x1 = x + 1 x2 = x + 2 y1 = y + 1 y2 = y + 2 x1e = x - 1 x2e = x - 2 y1e = y - 1 y2e = y - 2 f w = w <= 8 g w = w >= 1 83 Qual o valor de qPos 1 9 ? O que h de errado? Reescreva qPos de forma a contornar o problema encontrado. 10.4. SELECIONANDO TERMOS DE UMA TUPLA Assim com precisamos compor uma tupla na descrio dos mapeamentos de uma funo, tambm precisamos decomp-la. Quando uma funo possui tuplas como parmetros, para usar seus termos necessrio que se possa referenci-los. lustraremos aqui vrias formas, utilizando a definio que soluciona o seguinte problema: =eseja!os deter!inar a dist0ncia entre dois pontos no plano# 1. Utilizando a clusula 87ere : dist p1 p2 = sqrt (dx^2 + dy^2) where dx = x1 - x2 dy = y1 - y2 (x1,y1) = p1 (x2,y2) = p2 Observe a elegncia da definio da tupla (x1, y1). Por anlise da definio, sabemos que o tipo de p1 e p2 tupla de 2 termos. Quando submetemos a avaliao de uma instncia, o sistema casa p1 com um par de valores e a partir da pode determinar o valor de cada termo de nossa tupla (x1, y1). $ %ist &'.'('.') &".'(".') -- o sistema casa p1 com (0.0,0.0) -- e p2 com (5.0,5.0) -- logo x1=0.0, y1=0.0, x2=5.0 e y2=5.0 #.'#1'# 2. Construindo funes seletoras : prim (x,y) = x seg (x,y) = y dist p1 p2 = sqrt (dx^2 + dy^2) where dx = prim p1 - prim p2 dy = seg p1 - seg p2 Com esta abordagem, podemos generalizar e construir funes seletoras para tuplas com uma quantidade de termos qualquer. importante destacar que tuplas com quantidade diferente de termos precisam ter o seu prprio elenco de funes seletoras. Em particular, para o caso acima, onde o nmero de termos 2, o prprio HUGS j possui as funes seletoras, fst (primeiro) e snd (segundo). Poderamos t-las usado diretamente, mas preferimos ilustrar como se constri. Podemos reescrever a soluo usando-as. 84 dist p1 p2 = sqrt (dx^2 + dy^2) where dx = fst p1 - fst p2 dy = snd p1 - snd p2 3. Explicitando os componentes: dist (x1,y1) (x2,y2) = sqrt (dx^2 + dy^2) where dx = x1 - x2 dy = y1 - y2 Exerccios: 1. Escreva uma funo que determina as distncias entre trs pontos no plano cartesiano, duas a duas. Tanto os pontos dados como entrada, como as distncias calculadas devem ser representadas por tuplas. Na resposta, cada par de pontos deve ser exibido, seguido da distncia existente entre eles. 2. Refaa o exemplo de clculo de equaes de 2 grau, exibindo apenas as razes reais da equao. 3. Dados a sua data de nascimento e a data atual, informe qual a sua idade. 85 11. VALIDAO DE DADOS 11.1. INTRODUO Como sabemos, toda funo tem um domnio e um contradomnio. Em Hugs, quando tentamos avaliar uma instncia de uma definio, usando valores fora desse domnio, podemos receber como resposta mensagens nem sempre esclarecedoras. Quando se trata do usurio de nosso programa, esta situao se mostra mais indesejvel ainda. Aqueles que constroem a definio podem discernir com mais facilidade a natureza dos problemas que ocorrem durante o seu desenvolvimento. O mesmo no se pode esperar de algum que no tem conhecimento dos elementos internos de nossas definies. Tipicamente os erros sero de duas naturezas. Pode ser que o tipo da instncia esteja correto, mas nossa definio use alguma funo primitiva que no se aplica ao valor da instncia. Por exemplo, se temos a definio:
f x y z = div x y + z e se a submetemos a uma instncia onde ( -, teremos como resultado algo da seguinte natureza: ? f 5 0 3 Program error: {5 `div` 0} ? Um outro tipo de problema ocorre quando o tipo de algum parmetro da nossa instncia no casa com o tipo inferido pelo Hugs. Por exemplo, se usamos um valor do tipo ;loat para , obtemos: ? f 5 1.0 3 ERROR: Type error in application *** expression : f 5 1.0 3 *** term : 1.0 *** type : Float *** does not match : Int Nesta seo trataremos do primeiro caso, deixando o segundo para outra oportunidade. 11.2. CARACTERIZANDO A SITUAO Voltemos definio anteriormente apresentada:
86 f x y z = div x y + z Neste caso, o tipo inferido pela linguagem para os dois primeiros parmetros o tipo :nte,er, visto que o operador di$ s se aplica a valores deste tipo. Portanto o universo ser formado por todos os possveis ternos de valores onde os dois primeiros so do tipo :nte,er e o ltimo, do tipo obtido pela unio dos tipos ;loat e :nte,er# Chamemo-lo de F. Entretanto, o domnio de nossa funo no exatamente o conjunto de constantes de F, posto que a funo no est definida para as constantes de F cujo segundo elemento nulo. Desejamos construir uma funo que seja uma extenso da funo original e ao mesmo tempo lhe sirva como uma casca de proteo contra as violaes de domnio. Precisamos escolher um contradomnio da funo estendida (fx). Um candidato natural o contradomnio da funo original (f). E a imagem de nossa funo, o que podemos dizer dela? Ser que ela igual ao contradomnio? Ou ser que temos um subconjunto para o qual no exista um mapeamento? 11.3. CASO GERAL (IMAGEM IDNTICA AO CONTRADOMNIO) No caso geral, podemos encontrar duas situaes: ou o contradomnio idntico imagem, ou a determinao dos elementos que no pertencem imagem pode no ser fcil. Com isto no dispomos de valores no contradomnio que possam ser utilizados para mapearmos os valores que violam o domnio de f. Neste caso podemos estender o contradomnio (CD) de tal modo que o novo escolhido incorpore um valor que ser a imagem da extenso de f. Uma soluo geral bastante conveniente construir um novo contradomnio (NCD) para fx (extenso de f), formado por pares onde o primeiro elemento do tipo boolean e o segundo do contradomnio de f. Temos ento a seguinte estrutura: NCD(fx) = (boolean, CD(f)) Para valores de x no domnio de f, teremos: fx x = (True, f x ) Para os valores de x fora do domnio teremos: 87 fx x = (False, k ) onde 9 um valor qualquer pertencente ao contradomnio de f. Para o nosso exemplo inicial teramos ento: fx x y z = if invalido x y z then (False,0) else &*rue( f x y z) where invalido x y z = ... f x y z = div x y + z como se estivssemos criando uma casca protetora para a f# 11.4. FUNES QUE APRESENTAM PROBLEMAS EM VRIOS PARMETROS Quando uma funo possui vrios parmetros, pode ocorrer que mais de um deles dem origem questo que aqui levantamos. Quando isso ocorre, pode ser relevante caracterizar a situao apropriadamente. Neste caso podemos usar um conjunto mais variado de constantes do que as constantes booleanas, permitindo que possamos associar com cada erro uma constante diferente. Podemos tomar como exemplo o problema do movimento dos cavalos no jogo de xadrez, especificamente a soluo genrica que produzimos com a funo !o$ ! x onde ! o nmero do movimento, x a linha atual e a coluna atual. Os trs parmetros so vlidos apenas para o intervalo [1, 8]. Portanto !o$ no est definida para os valores pertencentes ao subconjunto do universo formado por todas as triplas onde pelo menos um dos elementos no pertence ao intervalo [1, 8]. Por outro lado, o contradomnio o conjunto booleano, portanto s possui duas constantes e ambas j esto comprometidas. Se quisermos distinguir os trs tipos de violao do domnio (movimento invlido, posio invlida, ambos invlidos) precisaremos usar um conjunto com pelo menos quatro constantes. Podemos ento definir a funo "movx da seguinte forma:
mo+, m x y = if not validom then if not validop then (3, False) else (1, False) else if not validop then (2, False) else (0, mo+ m x y) 11.5. CASO PARTICULAR (IMAGEM DIFERENTE DO CONTRADOMNIO) 88 Suponha que existe pelo menos um elemento 9 que no pertence imagem, ou seja, a imagem est contida no contradomnio. Podemos construir uma extenso de f de tal forma que os elementos que no pertenam ao domnio sejam mapeados neste 9 e os demais sejam mapeados diretamente pela f. Quando existe tal 9, nosso problema est resolvido. Basta que o usurio saiba que, quando a avaliao resultar em 9, significa que a funo no se aplica para aquela instncia. Voltemos funo f introduzida anteriormente. Podemos, usando a proposta desta seo, reescrever a sua definio, da seguinte forma:
fx x y z = if invalido x y z then k else f x y z where k = ... invalido x y z = ... f x y z = div x y + z nfelizmente, para esse caso, o 9 no existe (prove!). Voltemos ao movimento do cavalo. Nesse caso, especificamente, porque o contradomnio original o conjunto booleano, poderamos ter tomado outro caminho. Poderamos usar nmeros negativos para indicar os trs tipos de violao do domnio, a constante inteira - para representar ;alse e a constante inteira % para representar Frue, eliminando com isso a necessidade de um novo domnio formado por pares. Vejamos como fica esta definio:
mo+, m x y = if not validom then if not validop then (-3) else (-1) else if not validop then (-2) else if mo+ m x y then 1 else 0 11.6. UM EXEMPLO - RAIZES DE UMA EQUAO DO 2O. GRAU Voltemos ao problema de determinar as razes de uma equao do segundo grau. J sabemos que elas so duas e que podemos fazer uma nica funo para descrev-las, usando tupla. Sabemos ainda que o universo definido pelos tipos dos trs parmetros (a, b, c), maior que o domnio da funo. Ou seja, a funo no est definida para instncias de a, b e c, onde se verifica a seguinte desigualdade: (b^2) + (4 * a * c) < 0 Precisamos, portanto de uma funo estendida. 89 re2gx a b c = ¬ (delta < 0)( if delta < 0 then (0,0) else (x1, x2)) where delta = (b ^ 2) + (4 * a * c) x1 = ((-b) + sqrt delta) / (2 * a) x2 = ((-b) - sqrt delta) / (2 * a) Ou, de outra forma: re2gx a b c = if delta < 0 then (False, (0, 0)) else (True, (x1, x2)) where delta = (b ^ 2) + (4 * a * c) x1 = ((-b) + sqrt delta) / (2 * a) x2 = ((-b) - sqrt delta) / (2 * a) Exerccios 1. Explique por que no existe o valor k que apie a definio da funo fx; 2. Faa uma funo estendida para f x y = 1/(x+ y) 3. Fornea dois exemplos de funes para as quais possvel criar uma funo estendida na qual se possa usar constantes que no pertencem garantidamente imagem da funo original 90 12. LISTAS 12.1. INTRODUO A maioria dos itens de informao de nosso interesse est agrupada, dando origem a um conceito muito importante para a resoluo de problemas, o agrupamento. Freqentemente estamos interessados em manipular esses agrupamentos para extrair informaes, definir novos itens ou avaliar propriedades desses agrupamentos. Tratamos anteriormente das tuplas, que so agrupamentos de tamanho predefinido e heterogneo, ou seja, cada elemento que participa do agrupamento pode ser de um tipo diferente. Agora estamos interessados em explorar outro tipo de agregao, as listas. Este novo tipo, em HUGS, caracteriza-se por agregar quantidades variveis de elementos desde que todos eles sejam de um mesmo tipo. Vivemos cercados de listas. Elas esto em qualquer lugar onde precisamos registrar e processar dados. Vejamos alguns exemplos: 1. Lista de nmeros pares; 2. Lista dos livros lidos por uma pessoa; 3. Lista dos amigos que aniversariam em um dado ms; 4. Lista dos presidentes corruptos; 5. Lista dos vereadores decentes; 6. Lista das farmcias enganadoras; 7. Lista das disciplinas que j cursei; 8. Lista dos lugares que visitei; 9. Lista dos nmeros feios; 10. Lista dos nmeros primos; 11. Lista das posies para as quais um cavalo pode se deslocar; 12. Lista das palavras de um texto; 13. Lista dos erros provocados pelo Windows; 14. Lista dos prmios Nobel ganhos pelo Bertrand Russel; 15. Lista dos ttulos conquistados pelo Nacional Futebol Clube; 16. Listas dos fun+s compostos pelo Noel Rosa. Destas, algumas so vazias, outras so finitas e algumas infinitas. 12.2. CONCEITOS BSICOS : Uma lista uma seqncia de zero ou mais elementos de um mesmo tipo. Entende-se por seqncia uma quantidade qualquer de itens dispostos linearmente. Podemos representar uma lista pela enumerao dos seus elementos, separados por vrgulas e cercados por colchetes. [ e%, e2, .., en] Por exemplo: 91 1. [ ] 2. [1,3,5,7,9] 3. ['a', 'e', 'i', 'o', 'u'] 4. [(22,04,1500), (07,09,1822), (31,03,1964)] 5. [[1,2,5,10], [1,11], [1,2,3,4,6,12], [1,13], [1,2,7,14], [1,3,5,15]] importante ressaltar que em uma lista podemos falar do primeiro elemento, do quinto, do ensimo, ou do ltimo. Ou seja, h uma correspondncia direta entre os nmeros naturais e a posio dos elementos de uma lista. Este ltimo fato nos lembra de um equvoco freqente, que queremos esclarecer de sada. A ordem que se adota em listas, por ser baseada nos nmeros naturais, comea do zero. Ou seja, o primeiro elemento de uma lista tem o nmero de ordem igual a zero. Por exemplo, na relao acima apresentada, a primeira lista vazia. Na lista do item 4 o elemento de ordem 1 a tupla (07, 09,1822) e na lista do item 5, o elemento de ordem zero (0) a lista [1,2,5,10]. Quanto ao tipo, podemos dizer que a segunda lista uma lista de nmeros, a terceira uma lista de caracteres, a quarta uma lista de triplas de nmeros e a quinta uma lista de listas de nmeros. Qual ser o tipo da primeira lista? Uma lista vazia de natureza polimrfica, isto , seu tipo depende do contexto em que seja utilizada, como veremos em momento oportuno. 12.3. FORMAS ALTERNATIVAS PARA DEFINIO DE LISTAS Alm da forma bsica, acima apresentada, tambm conhecida como enumerao, onde explicitamos todos os elementos, dispomos ainda das seguintes maneiras: definio por intervalo, definio por progresso aritmtica e definio por compreenso. As duas primeiras formas so apresentadas a seguir e a terceira, posteriormente. Definio por IntervaIo De uma forma geral, podemos definir uma lista explicitando os limites inferior e superior de um conjunto conhecido, que possua uma relao de ordem entre os elementos, no seguinte formato: [<limite inferior> .. <limite superior>] Exemplos: abaixo listamos algumas submisses de listas definidas por intervalo e as correspondentes respostas do sistema HUGS. 92 1 -relu%e> [1..5] [1,2,3,4,5] ! -relu%e> [-2..2] [-2,-1,0,1,2] 3 -relu%e> [10..5] [] 4 -relu%e> [-5.0..5.0] [-5.0,-4.0,-3.0,-2.0,-1.0,0.0,1.0,2.0,3.0,4.0,5.0] " -relu%e> [-1..-5] ERROR: Undefined variable "..-" 6 -relu%e> [-1..(-5)] [] # -relu%e> [1.6..10.0] [1.6,2.6,3.6,4.6,5.6,6.6,7.6,8.6,9.6] . -relu%e> [1.5..10.0] [1.5,2.5,3.5,4.5,5.5,6.5,7.5,8.5,9.5,10.5] Podemos observar aqui algumas aparentes surpresas: no exemplo 4 podemos observar que o HUGS considera sempre o intervalo de uma unidade entre os elementos da lista definida. No exemplo 5, temos uma questo de ambigidade, 0 sinal de menos logo aps os dois pontos no entendido e o sistema sinaliza erro. O exemplo 6 contorna essa dificuldade. Deixo ao leitor buscar explicao para o que ocorre com os exemplos 7 e 8. Definio por Progresso Aritmtica Podemos definir qualquer progresso aritmtica por uma lista utilizando a seguinte notao: [<1o. termo>, <2o. termo> .. <limite superior>] Exemplos: observemos as definies a seguir e as respectivas respostas do sistema HUGS. Podemos perceber que dependendo do segundo termo da definio o sistema interpreta a P.A. como crescente ou decrescente. 1 -relu%e> [1,2..6] [1,2,3,4,5,6] ! -relu%e> [-5,2..5] [-5,2] 3 -relu%e> [-5,2..15] [-5,2,9] 4 -relu%e> [-5,2..16] [-5,2,9,16] " -relu%e> [1,1.1 .. 2] [1.0,1.1,1.2,1.3,1.4,1.5,1.6,1.7,1.8,1.9,2.0] 6 -relu%e> [6,5..0] [6,5,4,3,2,1,0] # -relu%e> [5,6..5] [5] . -relu%e> [3,7..1] [] 93 12.4. OPERAES BSICAS As listas, como j dissemos, so elementos da linguagem que podemos utilizar para descrever valores estruturados. Como nos demais tipos da linguagem, valores descritos por listas podem e devem ser usados na descrio de novos valores atravs da utilizao de operaes definidas sobre eles. A seguir so apresentadas algumas dessas operaes. eIem : avalia se um determinado elemento membro de uma lista. eIem <elemento> <lista> ExempIos: Main> elem 5 [-5..15] True Main> elem (-10) [-5..15] False Main> elem 20 [-5..15] False Main> elem 15 [-5..15] True Iength : descreve o tamanho de uma lista. Iength <lista> ExempIos: -relu%e>length [1..100] 100 (2528 reductions, 3442 cells) -relu%e>length [1..100] + length [1..100] 200 (5050 reductions, 6873 cells) -relu%e> a + a where a = length [1..100] 200 (2530 reductions, 3442 cells) indexao : podemos descrever cada termo de uma lista como um novo valor. Para tanto basta indicarmos a posio do elemento dentro da lista, considerando que o primeiro elemento tem a ordem zero. <lista>!!<ndice> ExempIos: 1 -relu%e> [3,4,6,74,45]//0 3 ! -relu%e> [3,4,6,74,45]//4 45 3 -relu%e> [1,5..20]//3 13 4 -relu%e> [[[1],[2,3,4]]]//0//1//2 4 " -relu%e> [3,4,6,74,45]//3 74 94 6 -relu%e> [3,4,6,74,45]//3 + [2,3]//0 76 # -relu%e> [3,4,6,74,45]//(0+1) 4 . -relu%e> [3,4,6,74,45]//(i+j) where i=3; j=0 74 0 -relu%e> [84,76,23,124,763,23]//([0,1,2,3]//2) 23 1' -relu%e> [1,5..20]!!5 Program error: Prelude.!!: index too large concat : descreve uma nova lista obtida pela concatenao de uma lista de listas. concat )lista de listas* ExempIos: 1 -relu%e>1on1at [[1..5],[1,10..100],[9]] [1,2,3,4,5,1,10,19,28,37,46,55,64,73,82,91,100,9] (473 reductions, 747 cells) ! -relu%e>1on1at [[1]] [1] (25 reductions, 35 cells) 3 -relu%e> length (1on1at [[1..5],[1,10..100],[9]]) 18 (451 reductions, 638 cells) 4 -relu%e> 1on1at [[length [10,87..500]], [length [10,87,500]]] [7,3] (228 reductions, 309 cells) " -relu%e> length (1on1at [[length [10,87..500]], [length [10,87,500]]]) 2 (30 reductions, 35 cells) Pode tambm ser usado com a sintaxe infixada usando o operador ++ ExempIos: 1 -relu%e> [1] ++ [1] [1,1] (33 reductions, 48 cells) ! -relu%e> [1] ++ [] [1] (23 reductions, 33 cells) 3 -relu%e> [ ] ++ [ ] ERROR: Cannot find "show" function for: *** Expression : [] ++ [] *** Of type : [a] 4 -relu%e> [1..5] ++ [1,10..100] ++ [9] [1,2,3,4,5,1,10,19,28,37,46,55,64,73,82,91,100,9] (467 reductions, 738 cells) construtor : descreve uma nova lista onde o primeiro termo um elemento dado e os demais so os componentes de uma lista tambm dada. 95 <elemento> : <lista> ExempIos: 1 -relu%e> 3: [4,5,6] [3,4,5,6] ! -relu%e> [3]:[[4,5,6]] [[3],[4,5,6]] 3 -relu%e> [3: [4,5,6]] [[3,4,5,6]] 4 -relu%e> [3: [4,5,6]]!!0 [3,4,5,6] " -relu%e> [3: [4,5,6]]!!0!!3 6 head : descreve o primeiro elemento de uma lista. taiI : descreve uma sublista contendo todos os elementos, exceto o primeiro elemento de uma lista dada.
Teorema importante: head xs : (taiI xs) = xs
Iast : descreve o ltimo elemento de uma lista.. init : descreve uma sublista contendo todos os elementos, exceto o ltimo elemento de uma lista dada.
Teorema importante: init xs ++ [last xs] = xs mportante: As funes head, last, init e tail no possuem em seu domnio a lista vazia. Portanto ela no deve ser instncia de avaliao para qualquer uma delas. <elemento> : <lista> ExempIos: 96 1 Hugs> head [1,2,3,4,5,6] 1 ! Hugs> tail [1,2,3,4,5,6] [2,3,4,5,6] 3 Hugs> tail [1,2,3,4,5,6] [2,3,4,5,6] 4 Hugs> last [1,2,3,4,5,6] 6 " Hugs> init [1,2,3,4,5,6] [1,2,3,4,5] 6 Hugs> x == (head x : tail x) where x = [10..15] True # Hugs> init [10..15] ++ tail [10..15] [10,11,12,13,14,11,12,13,14,15] 12.5. DEFINIO POR COMPREENSO Uma lista pode ser descrita atravs da enumerao de seus elementos, como j vimos atravs de vrios exemplos. Esta forma tambm denominada de definio por extenso, visto que todos os componentes precisam ser explicitados. Podemos tambm descrever listas atravs das condies que um elemento deve satisfazer para pertencer a ela. Em outras palavras, queremos descrever uma lista atravs de uma inteno. Esta forma anloga que j conhecemos para descrio de conjuntos. Por exemplo, usual escrever a notao abaixo para descrever o conjunto formado pelo quadrado dos nmeros naturais menores que 10. P = {quadrado de x | x naturaI e menor que 10} ou ainda mais formalmente, P = { x 2 | x pertence a N e x <10} Podemos observar, no lado direito da definio, que ela formada por duas partes. A primeira uma expresso que descreve os elementos, usando para isso termos variveis que satisfazem condies de pertinncia estabelecidas pela segunda parte. expresso x^2 varivel x pertinncia x pertence a N e x <10 Em nosso caso, no estamos interessados em descrever conjuntos e sim listas. E isso tem algumas implicaes prticas. Por exemplo, em um conjunto a ordem dos elementos irrelevante, para listas no. bom lembrar ainda que em uma lista, o mesmo valor pode ocorrer varias vezes, em diferentes posies. A sintaxe que usaremos a seguinte: [ <expresso> | <pertinncia> ] 97 Onde: <expresso> - expresses usuais em Haskell para definio de valores, inclusive condicionais. <pertinncia> - descrio dos elementos a serem considerados para avaliao da <expresso>. A pertinncia formada por uma seqncia de qualificadores de dois tipos de construo: os geradores e os predicativos. Os geradores descrevem uma lista de onde se originam os elementos a serem considerados, usando a sintaxe:
2termo> 2- 2lista> Por exemplo, vejamos a descrio da lista dos quadrados dos nmeros menores que 5. Prelude> [x^2| x<-[0..4]] [0,1,4,9,16] Os predicativos so expresses descrevendo valores booleanos, envolvendo termos j definidos anteriormente (inclusive por geradores). Vejamos o exemplo a seguir, onde descrevemos uma sublista de nmeros mpares, tendo como origem de gerao uma lista definida por uma Progresso Aritmtica. Prelude> 3, 4 ,2-31(4..1''5(o%% ,5 [1,7,13,19,25,31,37,43,49,55,61,67,73,79,85,91,97] importante destacar que as expresses de pertinncia so avaliadas da esquerda para direita. Por exemplo, se na expresso acima, primeiro colocssemos a expresso booleana "odd x", o sistema acusaria um erro, visto que ao tentar a avaliar "odd x", a varivel "x" ainda no estaria definida. Prelude> 3, 4 o%% ,( ,2-31(4..1''55 ERROR: Undefined variable "x" sto s ocorre porque o sistema usa uma ordem pr-definida! Vejamos como usar este novo conceito na escrita de novos scripts. A seguir apresentamos a definio de trs novas funes. A primeira, sIpares, define uma sublista formada pelos quadrados dos elementos pares de uma lista dada. A segunda, Imenor, define uma sublista formada pelos elementos de uma dada lista, que so menores que um elemento fornecido. A terceira, pmaioresk, ilustra o uso da clusula if-then-eIse para a gerao de elementos de uma lista. 98 -- Dada uma lista 'xs' defina uma sublista formada -- pelo quadrado dos elementos que so pares -- slpares ,s = 3,6! 4 ,2- ,s( e+en ,5 Main> :l preparalista.txt Reading file "preparalista.txt": Hugs session for: E:\HUGS98\lib\Prelude.hs preparalista.txt Main> slpares 31(4.."'5 [16,100,256,484,784,1156,1600,2116] Main> slpares 334(6#(00(!3(1!(3(6#(005 [1156,144] -- -- Determinar a sublista de elementos menores que 'x' -- em uma lista 'xs -- lmenor , ,s = 3 7 4 7 2-,s( 7 2 ,5 ... Main> lmenor 4" 31("(6(.6(34(#6(1!(34(.6(005 [1,5,6,34,12,34] Main> lmenor 1 31("(6(.6(34(#6(1!(34(.6(005 [] Main> lmenor 1'' 31("(6(.6(34(#6(1!(34(.6(005 [1,5,6,86,34,76,12,34,86,99] -- -- Determinar a lista de elementos gerados condicionalmente -- a uma constante dada k. pmenores8 8 ,s = 3 if , > 8 then ,! else , 9 ! 4 , 2-,s5 ... Main> pmenores8 3' 31("(6(.6(34(#6(1!(34(.6(005 [1,25,36,88,36,78,144,36,88,101] Quando mais de um gerador utilizado, devemos levar em conta que para cada elemento do gerador mais a esquerda sero gerados todos os elementos dos geradores subseqentes. Vejamos o exemplo a seguir onde se descreve uma lista de pontos do plano cartesiano, localizados no primeiro quadrante e delimitado pelas ordenadas 3 e 5. 99
-- -- Determinar a lista dos pontos do plano dentro da -- regiao definida pela origem, a cordenada (eixo y) -- 5 e a abscissa (eixo x) 3. -- pontos = 3 &,(7) 4 , 2- 3'..35( 7 2- 3'.."55 ... Main> pontos 3(0,0),(0,1),(0,2),(0,3),(0,4),(0,5), (1,0),(1,1),(1,2),(1,3),(1,4),(1,5), (2,0),(2,1),(2,2),(2,3),(2,4),(2,5), (3,0),(3,1),(3,2),(3,3),(3,4),(3,5)5 Entre dois geradores podemos usar predicativos, como se observa no exemplo a seguir. -- -- Determinar a lista dos pontos do plano dentro da -- regiao definida pela origem, a cordenada (eixo y) -- 5 e a abscissa (eixo x) 3. Considere apenas os -- pontos onde x impar e y par. -- pontos1 = 3 &,(7) 4 , 2- 3'..35( o%% ,( 7 2- 3'.."5( e+en 7 5 ... Main> pontos1 [(1,0),(1,2),(1,4),(3,0),(3,2),(3,4)] 12.6. DEFINIO POR COMPREENSO - EXPLORANDO DETALHES Vamos explorar um problema onde exista mais riqueza de detalhes quanto ao uso de predicativos. Determinar os pares de valores, onde: o primeiro mltiplo do segundo; o primeiro dado pelos elementos impares de uma P.A de primeiro termo igual a 1, a razo igual 3 e o ultimo termo menor ou igual a 100; o segundo termo dado pelos elementos de uma P.A de primeiro termo igual a 1, a razo igual 4 e o ultimo termo menor ou igual a 50; um dos dois diferente da unidade; os dois termos so diferentes. 100 -- -- A funo paresE, traduz literalmente o enunciado. -- As P.A.'s so realizadas diretamente pelo mecanismo -- para definio de P.A. embutido na linguagem. -- Os predicativos foram inseridos na posio em que -- suas variveis j esto instanciadas. -- pares:1 = 3 &,(7) 4 , 2- 31(4..1''5( o%% ,( 7 2- 31(".."'5( &,;=1 44 7;= 1)( ,;=7( mo% , 7 == '5 ... Main> pares:1 3(7,1),(13,1),(19,1),(25,1),(25,5),(31,1),(37,1), (43,1),(49,1),(55,1),(55,5),(61,1),(67,1),(73,1), (79,1),(85,1), (85,5),(85,17),(91,1),(91,13),(97,1)5 Vejamos algumas observaes sobre a soluo acima apresentada: o predicado odd poderia ser colocado em qualquer lugar mais a frente, entretanto o desempenho cairia, visto que iramos produzir valores desnecessrios para "y"; a expresso (x/=1 || y/= 1) desnecessria visto que s ser falsa quando ambos, x e y, forem iguais a 1, mas nesse caso eles sero iguais e portanto falsificariam a expresso a seguir x/=y; podemos reescrever a expresso x <- [1,4..100] de tal maneira que gere apenas valores mpares e assim descartar a expresso odd x. Para tanto basta mudar a P.A. para [1,7..100]; pode-se observar que os valores de y que interessam so sempre menores que os de x ( j que y divisor de x). Portanto, a segunda P.A poderia ser substituda por [1,5..(x-1)]. Acontece que agora poderemos geram valores para y maiores que 50 e isto no interessa. O que fazer? Que tal substitu-la por: [1, 5..(if x < 50 then (x-1) eIse 50)] Eis ento uma nova verso para nossa funo: pares:! = 3 &,(7) 4 , 2- 31(#..1''5( 7 2- 31("..&if , 2 "' then &,-1) else "')5( ,;=7( mo% , 7 == '5 Podemos agora refletir sobre uma possvel generalizao para a nossa funo, considerando-se duas listas quaisquer. Neste caso, o esforo realizado para melhorar o 101 desempenho seria em vo porque no conhecemos a priori a natureza das duas listas. Nossa funo poderia ser: pares:3 ,s 7s = 3 &,(7) 4 , 2- ,s( o%% ,( 7 2- 7s( ,;=7( mo% , 7 == '5 Apenas a expresso (x/=1 || y/= 1) poderia ser eliminada. O objetivo de nossa verso original poderia ser obtido pelo uso da nova funo aplicada s listas especficas. Conforme se observa a seguir: Main> pares:3 31(4..1''5 31(".."'5 [(7,1),(13,1),(19,1),(25,1),(25,5),(31,1),(37,1),(43,1), (49,1),(55,1),(55,5),(61,1),(67,1),(73,1),(79,1),(85,1), (85,5),(85,17),(91,1),(91,13),(97,1)] &16.#. re%u1tions( !300! 1ells)
Agora podemos ainda inspecionar o que de fato acontece com o desempenho das trs verses.
Coloquemos os valores obtidos em uma tabela. verso redues c!lulas "ares#$ ((+,4 3((1- "ares#% 1.1(4 ((-14 "ares#& 1$+'+ (3,,( Aqui podemos constatar que a verso paresE3, bastante similar a paresE1, a menos da generalizao, possui eficincia bem superior. Lembre-se que a nica diferena a eliminao da comparao dos dois termos com o valor 1. Para ver o que est acontecendo, vamos construir ainda uma outra verso, similar a paresE1, eliminando a comparao citada e descrevendo as P.A.'s fora dos geradores, 102 assimilando-se assim verso paresE3. Veja que com isso a nova verso, que obtivemos partindo de paresE1, possui eficincia similar a paresE3. Confirma-se ento a observao de que a definio de listas dentro dos geradores produz perdas.
pares:4 = 3 &,(7) 4 , 2- ,s( o%% ,( 7 2- 7s( ,;=7( mo% , 7 == '5 here ,s = 31(4..1''5 7s = 31(".."'5 ... main> pares:4 [(7,1),(13,1),(19,1),(25,1),(25,5),(31,1),(37,1),(43,1), (49,1),(55,1),(55,5),(61,1),(67,1),(73,1),(79,1),(85,1), (85,5),(85,17),(91,1),(91,13),(97,1)] &16.#. re%u1tions( !300! 1ells) 12.7. OPERAES PARA DETERMINAR SUBLISTAS Existem algumas operaes predefinidas no Haskell para descrevermos sublistas de uma lista dada. Nada que no possa ser feito com o que j apresentamos at aqui. Entretanto o seu uso pode ajudar na prtica do reuso e contribuir bastante para a clareza de um programa. Grupo I - Considerando um tamanho especificado take k xs - define uma lista com os k primeiros elementos de uma lista xs. drop k xs - define uma lista com os elementos de xs, a partir do elemento seguinte aos k primeiros. Vejamos a ilustrao a seguir. Prelude> ta8e 3 [0..10] 3'(1(!5 (156 reductions, 221 cells) Prelude> %rop 3 [0..10] 33(4("(6(#(.(0(1'5 (358 reductions, 513 cells) Prelude> 3,s//i 4 i 2-3'..&8 - 1)55 here ,s = 3'..1'5< 8 = 3 3'(1(!5 (249 reductions, 336 cells) Prelude> 3,s//i 4 i 2-38..length ,s - 155 here ,s = 3'..1'5< 8 = 3 33(4("(6(#(.(0(1'5 (1591 reductions, 2072 cells) Prelude> &ta8e 3 31..1'5 99 %rop 3 31..1'5) == 31..1'5 *rue (658 reductions, 980 cells) 103 Na verdade podemos escrever uma descrio geral para take e drop usando listas por compreenso. Alm disso, sabemos que a concatenao de take e drop para um certo k, aplicado uma lista xs, igual prpria lista. take k xs = [xs!!i | i <-[0..(k - 1)]] drop k xs = 3,s//i 4 i 2-38..length ,s - 155 vale o seguinte teorema: take k xs ++ drop k xs = xs Grupo II - Considerando a satisfao de um predicado pred. VaIe Iembrar que um predicado uma funo cujo vaIor resuItante do tipo booIean. takeWhiIe pred xs - define uma lista com os primeiros elementos de uma lista xs satisfazendo o predicado pred. dropWhiIe pred xs - define uma lista com os elementos de xs, a partir do primeiro elemento que no satisfaz o predicado pred. Vejamos a ilustrao a seguir. Prelude> takeWhile even [1..10] [] Prelude> takeWhile odd [1..10] [1] Prelude> dropWhile odd [1..10] [2,3,4,5,6,7,8,9,10] Prelude> dropWhile even [1..10] [1,2,3,4,5,6,7,8,9,10] Prelude> takeWhile (<5) [1..10] [1,2,3,4] Prelude> dropWhile (<5) [1..10] [5,6,7,8,9,10] Prelude> takeWhile (<5) [1..10] ++ dropWhile (<5) [1..10] == [1..10] True
104 13. RESOLVENDO PROBLEMAS COM LISTAS Neste captulo desenvolveremos a soluo para alguns problemas mais complexos. A inteno apresentar o uso de estratgias de propsito geral que podem ser teis na resoluo de novos problemas. Discutimos tambm algumas questes relativas ao desempenho das solues. 13.1 PROBLEMA 1 : =ada u!a lista, deter!ine o seu !aior ele!ento Comecemos por definir, usando a linguagem de conjuntos, quem este elemento. Dizemos que k o maior elemento de um conjunto C, se e somente se, o subconjunto de C formado por todos os elementos maiores que k vazio. Em linguagem de lista isto equivale a dizer que se C uma lista, a sublista de C formada pelos caras de C maiores que k vazia. Vejamos como fica a codificao em Haskell. -- -- definindo uma funo para determinar, em uma lista, -- a sublista dos elementos maiores que um dado x -- maiores x xs = [ y | y <- xs, y > x] -- -- -- Em listas, podemos ter elementos repetidos, e em particular -- podemos ter vrios exemplares do maior elemento. -- Chamemos esses caras de os "maiorais da lista". -- Vamos construir uma funo para descrev-los. -- maiorais xs = [ k | k <- xs, maiores k xs == [] ] -- -- Como eles so todos idnticos podemos tomar o primeiro deles -- como soluo de nosso problema. -- maximo xs = head (maiorais xs) -- 13.2 PROBLEMA 2: =ada u!a lista, $erifique se ela n4o decrescente# Como aquecimento, vamos considerar listas de nmeros e a noo usual de ordem no decrescente. Antes de programar, vamos resgatar a definio de seqncias no decrescentes.
=efini34o: Uma seqncia S est em ordem no decrescente se e somente se qualquer um de seus elementos menor ou igual aos seus sucessores. Em outras palavras, podemos dizer que a coleo de elementos de S que so maiores que seus sucessores vazia.
105 -- -- lista dos maiores que os sucessores -- lms1 xs = [ xs!!i | i <-[0..length xs - 2], j <-[i+1..length xs - 1], xs!!i > xs!!j] -- -- a sequncia est ordenada se a lista dos elementos -- que so maiores que algum sucessor vazia -- ord1 xs = (lms1 xs) == [] ... Main> or%1 31..1'5 True (10801 reductions, 13891 cells) Main> or%1 31'(0..15 False (405 reductions, 571 cells) 13.3. DISCUTINDO EFICINCIA: Observando a avaliao da funo ord1, apresentada na seo anterior, para uma lista j ordenada, a lista [1,2,3,4,5,6,7,8,9,10], constatamos um nmero muito grande de redues. Convm questionar os motivos. Main> or%1 31..1'5 True (10801 reductions, 13891 cells) Main> or%1 31'(0..15 False (405 reductions, 571 cells) Em geral os motivos para um baixo desempenho so de duas naturezas: explorao inadequada das propriedades do problema e escolha inadequada dos mecanismos da linguagem. A seguir fazemos uma pequena explorao desses dois aspectos. 13.3.1. EXPLORANDO PROPRIEDADES DO PROBLEMA - Analisando a nossa definio anterior constatamos que ela diz mais do que precisamos. Ela avalia cada elemento com respeito a todos sucessores. Na verdade, nossa definio pode ser melhorada. Basta saber que cada elemento tem que ser menor ou igual ao seu sucessor imediato. Vamos reescrever a nossa definio: =efini34o : Uma seqncia S est em ordem no decrescente se e somente se qualquer um de seus elementos menor ou igual ao seu sucessor imediato. Em outras palavras, podemos dizer que a coleo de elementos de S que so maiores que seus sucessores imediatos vazia.
Vejamos ento a implementao em Haskell e sua aplicao s mesmas instncias do problema: 106 -- -- lista dos elementos maiores que o sucessor imediato -- lms2 xs = [ xs!!i | i <-[0..length xs - 2], xs!!i > xs!!(i+1)] -- ord2 xs = (lms2 xs) == [] ... Main> or%! 31..1'5 True (2236 reductions, 2885 cells) Main> or%! 31'(0..15 False (314 reductions, 449 cells) Podemos observar uma queda no nmero de reduKes da ordem de 79%!!! Para o pior caso, ou seja, quando todos os elementos satisfazem a propriedade estabelecida. No melhor caso, onde o primeiro j no satisfaz a propriedade, a queda foi da ordem de 22%. Na utilizao de clulas, os ganhos foram da mesma ordem. 13.3.2. EXPLORANDO OS MECANISMOS DA LINGUAGEM - Uma outra investida que podemos fazer com respeito ao uso adequado dos mecanismos da linguagem. As solues acima apresentadas processam as listas atravs de ndices, ou seja, fazem um acesso aleatrio aos elementos da lista. Como j foi discutido, o acesso seqencial possui realizao mais eficiente. -- -- Para manusear os pares de adjacentes, ao -- invs de usar ndices, usemos a funo =ip -- aplicada a um par de listas construdas com base em ,s -- a)lista formada pelos elementos de xs, exceto o ltimo, -- que pode ser obtida com a funo init; -- b)lista formada pelos elementos de xs, exceto o primeiro, -- que pode ser obtida com a funo tail. -- Com isso obtemos uma lista formada pelos pares adjacentes. -- a%>a1entes ,s = =ip &init ,s) &tail ,s) ... Main> a%>a1entes 31..1'5 [(1,2),(2,3),(3,4),(4,5),(5,6),(6,7),(7,8),(8,9),(9,10)] (414 reductions, 822 cells) Main> a%>a1entes 31'(0..15 [(10,9),(9,8),(8,7),(7,6),(6,5),(5,4),(4,3),(3,2),(2,1)] (364 reductions, 667 cells) -- -- A nova funo para definir lista dos maiores -- que os sucessores. Agora trabalharemos com os pares -- lms3 ps = [ (x,y) | (x,y) <- ps, x>y] -- -- A nova verso de 'ord' -- ord3 xs = lms3 (adjacentes xs) == [] 107 ... Main> or%3 31..1'5 True (313 reductions, 483 cells) Main> or%3 31'(0..15 False (82 reductions, 145 cells) O ganho obtido para a instncia considerada foi de 86% no nmero de redues e de 83% no nmero de clulas utilizadas, com respeito segunda definio. Exerccios: 1. Verificar se uma cadeia de caracteres representa um nmero inteiro positivo. 2. Dada uma lista l, contendo uma quantidade igual de nmeros inteiros pares e mpares (em qualquer ordem), defina uma funo que, quando avaliada, produz uma lista na qual esses nmeros pares e mpares encontram-se alternados. 3. Dada uma lista xs, fornecer uma dupla contendo o menor e o maior elemento dessa lista. 4. Dadas duas listas de elementos distintos, determinar a unio delas. 108 14. PARADIGMA APLICATIVO 14.1. INTRODUO: A descrio de funes, como acontece com outras formas de representao de conhecimento, admite vrios estilos. Dependendo do problema que estamos tratando, um estilo pode ser mais conveniente que outros. Podemos dizer que influi muito na escolha, o quanto desejamos ou necessitamos, falar sobre como computar uma soluo. A descrio de solues tem um espectro de operacionalidade que vai do declarativo at o procedural, em outras palavras, do que desejamos computar ao como queremos computar. Uma situao que ocorre com freqncia na descrio de funes a necessidade de aplicar uma funo, de forma cumulativa, uma coleo de elementos. Em outras palavras, desejamos generalizar uma operao para que seu uso se estenda a todos os elementos de uma lista. Chamemos este estilo de paradigma aplicativo. Por exemplo, suponha que desejamos obter a soma de todos os elementos de uma lista. A operao adi34o (+) segundo sabemos, de natureza binria, ou seja, opera sobre dois elementos produzindo um terceiro. Para operar sobre todos os elementos de uma lista de forma a obter a soma total, podemos oper-los dois a dois, obtendo com isso resultados intermedirios, que por sua vez podero ser novamente operados e assim sucessivamente at que se obtenha o resultado final. Observemos o processo para uma instncia: M+ter a so!a dos ele!entos da lista N5, O, 3, L, %5, %&P Podemos tomar os pares de elementos, primeiro com segundo, terceiro com quarto e assim sucessivamente. expresso nova expresso reduo + [5, 9, 3, 8, 15, 16] + [14, 3, 8, 15, 16] 5 + 9 = 14 + [14, 3, 8, 15, 16] + [14, 11, 15, 16] 3 + 8 = 11 + [14, 11, 15, 16] + [14, 11, 31] 15 + 16 = 31 + [14, 11, 31] + [25, 31] 14 + 11 = 25 + [25, 31] + [56] 25 + 31 = 56 + [56] 56 A escolha dos pares, no caso da adio, no importa. As redues poderiam ser aplicadas em qualquer ordem, visto que a operao adio comutativa. Se, pode ser qualquer uma, ento podemos estabelecer uma ordem, como por exemplo, da esquerda para a direita, usando em cada nova reduo o elemento resultante da reduo anterior.
Expresso nova expresso Reduo + [5, 9, 3, 8, 15, 16] + [14, 3, 8, 15, 16] 5 + 9 = 14 109 + [14, 3, 8, 15, 16] + [17, 8, 15, 16] 14 + 3 = 17 + [17, 8, 15, 16] + [25, 15, 16] 17 + 8 = 25 + [25, 15, 16] + [40, 16] 25 + 15 = 40 + [40, 16] + [56] 40 + 16 = 56 + [56] 56 Em DQR> existe um operador que permite a descrio de computaes desta natureza. Este operador denomina-se foldl. A sua utilizao requer ainda que seja indicado um $alor especial para a operao considerada. Este elemento tomado como ponto de partida para as redues, ou seja, a primeira aplicao da operao sobre o $alor especial e o primeiro elemento da lista. Eis a sintaxe: foIdI <operao> <valor especial> <lista> A ordem estabelecida para as redues semelhante ilustrao acima, ou seja, caminha-se da esquerda para direita, usando o resultado da reduo anterior para a nova reduo. expresso nova expresso foldl op $e [x0, x1, x2,...,xn] op [(op ve x0), x1, x2,...,xn] op [(op (op ve x0) x1), x2,...,xn] op [op((op (op ve x0) x1) x2),...,xn] op [op(op((op (op ve x0) x1) x2)... xn)] op(op((op (op ve x0) x1) x2)... xn) Merece aqui um destaque, foldl na verdade um funo, com 3 parametros, sendo que um deles uma outra funo. Dizemos neste caso que foldl uma funo de segunda ordem. O tipo da operao tem que ser: 2alfa> -> 2?eta> -> 2?eta> onde : <alfa> pode ser do mesmo tipo de <beta>; <beta> tem que ser do tipo dos componentes da lista; o valor especial do tipo <alfa> Vejamos ento a definio do operador su! disponvel no HUGS e cujo objetivo a descrio da soma dos elementos de uma lista.
sum xs = fol%l (+) 0 xs OBS: 110 1. Os operadores infixados so indicados entre parntesis. 2. O valor especial o zero, visto que no desejamos que o valor especial modifique o resultado da soma de todos os elementos. Entretanto, ele joga um papel muito especial quando a lista for vazia. prelude> sum [] 0 3. Em exemplos futuros veremos outros usos para o valor especial. 14.2. ALGUMAS OPERAES IMPORTANTES: Assim como a somatria, existem outras operaes importantes, de grande utilidade, que podem ser obtidas pelas seguintes equaes usando foldl# -- -- produto -- valor especial = 1 -- pro%u1t xs = fol%l (*) 1 xs -- -- conjuno -- valor especial = True -- an% xs = fol%l (&&) *rue xs -- -- disjuno -- valor especial = False -- or xs = fol%l (||) False xs ExempIo 01: Usando "product para descrever o fatorial de um nmero. Sabemos que: / fatorial de um n8mero natural n%! 4 igual ao produto de todos os n8meros naturais de 1 at4 n -- -- definio de fatorial -- fat n = pro%u1t 31..n5 ... $ fat " 120 $ fat ' 1 $ fat 1 1 ExempIo 02: Podemos usar a disjuno (or) generalizada para definir uma funo que avalia se em uma dada lista de nmeros pelo menos um deles impar. 111 -- -- pelo menos um impar -- um@mpar xs = or 3odd x 4 x <- xs5 ... ? um@mpar [2,2,2,2,2,2,2,2] False ? um@mpar [2,2,2,1,2,2,2,2] True ? um@mpar [] False
14.3. O MENOR ELEMENTO DE UMA LISTA: Estamos interessados em obter uma funo que associe uma lista xs com o elemento de xs que seja o menor de todos. Anteriormente j apresentamos uma verso para a funo que descreve o maior elemento de uma lista, que bastante similar a esta. Na oportunidade exploramos uma propriedade que o elemento !aior de todos deve satisfazer. No caso do menor elemento, podemos explorar uma propriedade anloga. >m uma lista xs1 di$emos que + 4 o menor elemento de xs1 se e somente se a sublista de xs formada por elementos menores que + 4 7a$ia -- -- menores descreve os elementos menores que um -- dado x em uma lista xs -- menores x xs = [ y | y <- xs, y < x] -- minimo descreve a sublista de xs dos elementos -- que no possuem menores que eles em xs -- minimos xs = [ k | k <- xs, menores k xs == [] ] -- -- Como eles so todos idnticos podemos tomar o -- primeiro deles -- como soluo de nosso problema. -- menorA' xs = head (menores xs) Vamos explorar agora o problema a partir da generalizao da operao menor. Em sua forma bsica, a funo menor associa dois nmeros quaisquer com o menor entre eles. Precisamos identificar um elemento que no interfira no resultado para fazer o papel de $alor especial. Para a operao menor podemos observar que este papel pode ser desempenhado por qualquer um dos elementos da lista, visto que o menor entre dois valores idnticos o prprio valor. Como pode ser qualquer um, podemos escolher o primeiro elemento de xs (head). 112 -- -- menor de dois -- menor x y = if x < y then x else y -- -- menor da lista -- menorA xs = fol%l menor (head xs)xs ... ? menorA [5,5,4,4,4,6,6,6,3,3,3,11,1,0] 0 (84 reductions, 157 cells) ? menorA [5] 5 (5 reductions, 13 cells) ? menorA [ ] ERROR: Unresolved overloading *** type : Ord a => a *** translation : menorL [] Podemos observar aqui que a funo menorL parcial pois no se aplica a lista vazia. 14.4. INSERO ORDENADA E ORDENAO DE UMA LISTA: A ordenao de dados uma das operaes mais realizadas em computao. Em todos os computadores do mundo, faz-se uso intensivo dela. Este assunto muito especial e por isso mesmo profundamente estudado. Cabe-nos aqui fazer uma breve passagem pelo assunto, sem, contudo nos aprofundarmos nas questes de eficincia, que central no seu estudo. Ser que podemos usar o conceito de generalizao de uma operao para descrever a ordenao de uma lista? Que operao seria essa? 14.4.1. INSERO EM LISTA ORDENADA - Vamos comear discutindo outra questo mais simples: dada uma lista, com seus elementos j dispostos em ordem no decrescente, como descrever uma lista na mesma ordem, acrescida de um elemento tambm fornecido? Podemos observar que se a lista xs est em ordem no decrescente, com respeito ao elemento x (dado), podemos descrev-la atravs de dois segmentos: xs ( S!enores que x' ++ S!aiores ou i,uais a x' Para acrescentar x a xs, basta concatenar x entre os dois segmentos, obtendo a nova lista ys, assim: s ( S!enores que x e! xs' ++ [x] ++ S!aiores ou i,uais a x e! xs' 113 -- insero ordenada -- -- insor% xs x = ta8eBhile (< x) xs ++ [x] ++ %ropBhile (< x) xs ... Main> insord [] 10 [10] (27 reductions, 49 cells) Main> insord [10] 20 [10,20] (54 reductions, 84 cells) Main> insord [10,20] 30 [10,20,30] (79 reductions, 119 cells) Main> insord [10,20,30] 5 [5,10,20,30] (71 reductions, 110 cells) Main> insord [5,10,20,30] 25 [5,10,20,25,30] (126 reductions, 183 cells) 14.4.2. Ordenao - A APLICAO SUCESSIVA DE INSORD , CONFORME ILUSTRADA ACIMA, NOS D UMA PISTA PARA NOSSA GENERALIZAO. PODEMOS PEGAR CADA ELEMENTO DA LISTA A SER ORDENADA E INSERI-LO EM ORDEM EM OUTRA LISTA QUE SER PAULATINAMENTE CONSTRUDA, J ORDENADA. VAMOS SEGUIR O EXEMPLO ACIMA, ONDE DESEJAMOS ORDENAR A LISTA [10,20,30,5,25]: Iista parciaI novo eIemento reduo [] 10 [10] [10] 20 [10,20] [10,20] 30 [10,20,30] [10,20,30] 5 [5, 10, 20, 30] [5, 10, 20, 30] 25 [5, 10, 20, 25, 30] O ponto de partida, neste caso, a lista vazia. Vamos tomar ento a lista vazia como o valor especial. Vejamos como fica ento nossa definio para ordenao de listas. 114 -- ordenao -- or%ena xs = fol%l insord [] xs ... Main> or%ena [10,20,30,5,25] [5,10,20,25,30] (220 reductions, 344 cells) Main> or%ena [1..10] [1,2,3,4,5,6,7,8,9,10] (1055 reductions, 1505 cells) Main> or%ena [10,9..1] [1,2,3,4,5,6,7,8,9,10] (448 reductions, 712 cells)
14.5. INVERSO DE UMA LISTA: Dada uma lista xs, desejamos descrever a lista formada pelos elementos de xs tomados em ordem inversa. Para resolver o problema precisamos inventar uma funo bsica passvel de generalizao. Vamos comear descrevendo a funo insAntes. -- -- Insere um elemento antes do primeiro elemento -- de uma dada lista. Funo similar ao operador -- (:) exceto pela ordem invertida dos parmetros -- insCntes xs x = x : xs ... Main> insCntes [1..5] 0 [0,1,2,3,4,5] (171 reductions, 255 cells) Main> insCntes [] 0 [0] (22 reductions, 29 cells) Tentemos agora a generalizao. A inteno incluir cada elemento da lista xs que desejamos inverter, antes do primeiro elemento de uma lista que iremos construindo gradativamente. O $alor especial ser a lista vazia. Vejamos um exemplo onde inverteremos a lista [0, 1, 2, 3, 4] Iista parciaI novo eIemento reduo [] 0 [0] [0] 1 [1, 0] [1, 0] 2 [2, 1, 0] [2, 1, 0] 3 [3, 2, 1, 0] [3, 2, 1, 0] 4 [4, 3, 2, 1, 0] 115 Vamos ento usar o operador foldl para construir a generalizao desejada: -- inversao -- in+erte xs = fol%l insAntes [] xs ... Main> in+erte [0..4] [4,3,2,1,0] (172 reductions, 259 cells) Main> in+erte [4,3..4] [4] (74 reductions, 110 cells) ... Main> re+erse [4,3..4] [4] (74 reductions, 111 cells) Main> re+erse [0..4] [4,3,2,1,0] (171 reductions, 257 cells) Observe a similaridade entre o desempenho da funo reverse, pr-definida, com o desempenho da inverte que acabamos de definir. 14.6. INTERCALAO DE LISTAS: Dadas duas lista xs e ys, ambas em ordem no decrescente, desejamos descrever uma nova lista em ordem no decrescente, formada por todos os elementos das duas listas. Um processo bastante conhecido, chamado de +alance line, consiste em ir transferindo para a nova lista, os elementos de uma das listas de entrada, enquanto estes forem menores que os da outra. Quando a condio no mais satisfeita, fazemos o mesmo para a outra lista. Por exemplo, vamos intercalar as listas [2, 4, 6, 8] e [1, 3, 5]. Vejamos o desenrolar do processo. Iista parciaI Andamento em xs Andamento em ys Reduo [] [2, 4, 6, 8] [1, 3, 5] [1] [1] [2, 4, 6, 8] [3, 5] [1, 2] [1, 2] [ 4, 6, 8] [3, 5] [1, 2, 3] [1, 2, 3] [ 4, 6, 8] [5] [1, 2, 3, 4] [1, 2, 3, 4] [6, 8] [5] [1, 2, 3, 4, 5] [1, 2, 3, 4, 5] [6, 8] [] [1, 2, 3, 4, 5, 6] [1, 2, 3, 4, 5, 6] [ 8] [] [1, 2, 3, 4, 5, 6, 8] [1, 2, 3, 4, 5, 6, 8] [] [] Vamos precisar aqui de uma bela engenharia para a construo da funo a generalizar. Desta vez, nossa entrada formada por duas listas e como sabemos o 116 operador foIdI, a cada vez, processa um elemento de uma determinada lista. sto nos leva a ter que inventar tambm a lista a ser processada. A funo bsica pode ser inventada a partir da descrio de um passo do processo que denominamos de +alance line. Em cada passo temos como entrada uma lista parcialmente construda e duas listas parcialmente processadas. Como sada teremos mais um passo de construo da lista resultante e o andamento no processamento de uma das listas. Na lista em construo devemos inserir o menor entre os cabeas (head) das duas listas. Aquela que tiver o menor cabea dar origem a uma nova lista, da qual foi excludo o primeiro elemento, ou seja, ser idntica ao resto (tail) da lista escolhida.
117 -- -- Descrio de um passo do Balance Line. -- 1) Quando uma das duas listas for vazia a -- outras diretamente concatenada no final -- da lista em construo. -- passoDA (xs,ys,zs) = if (ys == []) then ([],[],zs++xs) else if (xs == []) then ([],[],zs++ys) else if (head xs <= head ys) then (tail xs,ys,zs++[head xs]) else (xs, tail ys,zs++[head ys]) ... ... aplicao sucessiva da funo passoDA intercalao ... das listas acima propostas ... Main> passoDA ([2,4,6,8],[1,3,5],[]) ([2,4,6,8],[3,5],[1]) (121 reductions, 219 cells) Main> passoDA ([2,4,6,8],[3,5],[1]) ([4,6,8],[3,5],[1,2]) (122 reductions, 222 cells) Main> passoDA ([4,6,8],[3,5],[1,2]) ([4,6,8],[5],[1,2,3]) (123 reductions, 225 cells) Main> passoDA ([4,6,8],[5],[1,2,3]) ([6,8],[5],[1,2,3,4]) (124 reductions, 228 cells) Main> passoDA ([6,8],[5],[1,2,3,4]) ([6,8],[],[1,2,3,4,5]) (128 reductions, 239 cells) Main> passoDA ([6,8],[],[1,2,3,4,5]) ([],[],[1,2,3,4,5,6,8]) (116 reductions, 223 cells) ... Main> passoDA ([1..5],[],[]) ([],[],[1,2,3,4,5]) (190 reductions, 329 cells) Main> passoDA ([],[1..5],[]) ([],[],[1,2,3,4,5]) (193 reductions, 339 cells) Main> passoDA ([1..3],[4..5],[]) ([2,3],[4,5],[1]) (219 reductions, 371 cells) A generalizao, de forma que possamos aplicar a funo gradativamente, de tal forma que todo o processo se complete, requer a inveno de uma lista sobre a qual ocorra a repetio. O nmero de aplicaes sucessivas ser no mximo igual soma dos comprimentos das listas a serem intercaladas. Podemos ento definir a aplicao sobre a lista dos nmeros de 1 at a soma dos tamanhos. 118 -- -- Funo para Intercalar duas listas ordenadas -- 1) a funo passoBL repetidamente aplicada, sobre -- o resultado obtido no passo anterior; -- 2) a funo passoBL binria, como exigido por foldl; -- 3) a aplicao sucessiva controlada pelo comprimento -- da lista resultante; -- 4) o resultado final obtido pela seleo do terceiro -- elemento da tripla atravs da primitiva "thd3" -- ?aAine xs ys = thrd3 (baLine3 xs ys) -- thr%3 (x,y,z) = z -- ?aAine3 xs ys = foldl passoBL (xs,ys,[]) [1..tr] where tr = (length xs)+(length ys) -- -- Descrio de um passo do Balance Line. -- 1) Quando uma das duas listas for vazia a -- outras diretamente concatenada no final -- da lista em construo. -- 2) O parmetro k apenas para estabelecer o tipo -- binrio exigido por foldl -- passoDA (xs,ys,zs) k = if (ys == []) then ([],[],zs++xs) else if (xs == []) then ([],[],zs++ys) else if (head xs <= head ys) then (tail xs,ys,zs++[head xs]) else (xs, tail ys,zs++[head ys]) ... Main> thr%3 (10,20,30) 30 (11 reductions, 12 cells) Main> ?aAine3 [1..3] [4..6] ([],[],[1,2,3,4,5,6]) (514 reductions, 896 cells) Main> ?aAine [1..3] [4..6] [1,2,3,4,5,6] (485 reductions, 784 cells) Main> ?aAine [1..5] [3..7] [1,2,3,3,4,4,5,5,6,7] (807 reductions, 1326 cells) Main> ?aAine [2, 4, 6, 8] [1, 3, 5] [1,2,3,4,5,6,8] (425 reductions, 696 cells) Exerccios: Use o paradigma aplicativo para resolver os problemas abaixo: 1.Dadas duas strings xs e ys, verificar se xs sublista de ys. 2.Definir a funo tWhile, que tenha o mesmo comportamento que a funo takeWhile. 3.Definir a funo dWhile, que tenha o mesmo comportamento que a funo dropWhile. 4.Verificar se uma string um palndrome (a string a mesma quando lida da esquerda para a direita ou da direita para a esquerda). 119 15. Processamento de Cadeias de Caracteres - primeiros passos 15.1. INTRODUO: Alm de nmeros, nosso mundo povoado por textos. Cada vez mais se torna presente o uso de computadores para nos auxiliar na tarefa de armazenar, recuperar e processar documentos. Neste captulo estamos interessados em fazer uma breve introduo ao uso de computadores nessas tarefas. O ponto de partida o tipo caracter (char), que nos permite representar textos na memria (principal e secundria) dos computadores. Veremos tambm como agrup-los para compor palavras, frases e por fim documentos. 15.2. O TIPO CHAR: O tipo char formado por um conjunto de smbolos. Outro nome usado para esta coleo alfabeto, ou seja, o conjunto de tomos que serviro de base para a construo de cadeias complexas, inclusive os textos usuais. Entre os smbolos citados podemos destacar trs agrupamentos relevantes, tais como: 1. As letras maisculas do alfabeto; 2. As letras minsculas do alfabeto; 3. Os algarismos arbicos; Estes trs agrupamentos gozam de uma propriedade muito importante. Dentro deles os smbolos possuem uma relao de ordem, de tal forma que podemos usar a noo usual de ordenao para letras e algarismos. Alm destes, podemos citar ainda os sinais de pontuao e alguns smbolos com funes especiais, como, por exemplo, indicador de final de linha de texto. Os smbolos so sempre apresentados em HUGS entre aspas simples, para que possam ser diferenciados dos nomes de parmetros, funes e outros. Por exemplo, a letra a deve ser denotada por TaU # A definio a seguir ajuda a esclarecer esta necessidade. f a = (a, 'a') Aqui usamos a letra "a trs vezes. A primeira, da esquerda para direita, nomeia um parmetro da funo "f. Na segunda ocorrncia, utilizamos o parmetro da funo para se constituir do primeiro elemento de uma tupla. J a terceira ocorrncia se refere constante "a. Vejamos as respostas do sistema para diferentes usos da funo f 120 1 Eain> f 3 (3,'a') ! Eain> f 5 (5,'a') 3 Eain> f a ERROR - Undefined variable "a" 4 Eain> f 'a' ('a','a') " Eain> f 'aa' ERROR - Improperly terminated character constant 6 Eain> f 'b' ('b','a') # Eain> f a where a='a' ('a','a') Nas situaes 1, 2, e 6, o parmetro a instanciado para os valores 3, 5 e 'b' , resultando nos pares (3, 'a'), (5,'a') e (b, 'a'). Na situao 4, o parmetro a instanciado para a constante 'a', produzindo o par ('a', 'a'). Na situao 3, o uso de a est incorreto, pois como no est entre as aspas simples, interpretado como um parmetro, que no est instanciado quando deveria estar. Na situao 7, ao contrrio da situao 3, o valor de a instanciado atravs da clusula where e a situao fica similar situao 4. A coleo total dos smbolos de nosso alfabeto forma uma seqncia de tal forma que podemos fazer o mapeamento entre a subseqncia de nmeros naturais, de zero (0) a 255 e a seqncia de smbolos. Duas funes bsicas permitem que se faa a converso entre as duas seqncias. Elas podem ser encontradas em uma biblioteca de funes diferente de Prelude.hs. Essa biblioteca se chama Data.Char. Para usar essas funes no seu interpretador HUGS, preciso executar o comando :I Data.Char no interpretador: 1. A funo c7r associa um nmero natural no intervalo [0,255] com o caracter correspondente. Por exemplo, c7r 64 = '@' e c7r 38 = '&'. 2. A funo ord faz o mapeamento inverso, ou seja, associa um smbolo com o nmero natural correspondente. Por exemplo, ord '?' = 63 e ord '%' = 37. A tabela a seguir apresenta alguns dos agrupamentos importantes. 121 AIgarismos Letras MaiscuIas Letras MinscuIas
,' a ,+ b ,, c 1-- d 1-1 e 1-( ' 1-3 ( 1-( ) 1-. i 1-$ * 1-' + 1-+ l 1-, , 11- n 111 o 11( " 113 - 114 r 11. s 11$ t 11' u 11+ v 11, . 1(- x 1(1 / 1(( 0 1(3 1 1(4 2 1(. 3 1($ 4 Alm das operaes ord e chr, podemos contar com os operadores relacionais que foram apresentados no Captulo 6. Da mesma forma que existem os operadores para 122 3( 5 5 33 6 34 7 3. 8 3$ 9 3' : 3+ ; 3, < 4- ( 41 ) 4( = 43 + 44 , 4. > 4$ ? 4' @ 4+ A 4, $ .- % .1 & .( B .3 C .4 D .. E .$ F .' G .+ : ., H $- I $1 = $( J $3 K $4 L $. M $$ N $' C $+ D $, # '- F '1 O '( P '3 Q '4 R '. S '$ T '' U '+ N ', V +- W +1 X +( Y +3 Z +4 T +. [ +$ \ +' ] ++ ^ +, _ ,- ` ,1 [ ,( / ,3 ] ,4 a ,. 0 ,$ 1 comparao de nmeros, existe os operadores relacionais que comparam dois smbolos, como no exemplo: Prelude> 'c' > 'h' False O significado de uma operao relacional sobre characteres determinado com base na ordem destes com respeito tabela apresentada para codificao desses smbolos. Assim, podemos dizer que: Se x e y so do tipo caractere, x > y se e somente se ord x > ord y Podemos agora construir algumas definies interessantes, conforme se apresenta nos quadros a seguir: -- -- +erifi1a se um %a%o sFm?olo G letra -- letra x = (maiuscula x) || (minuscula x) -- maiuscula x = pertence x ('A','Z') minuscula x = pertence x ('a','z') pertence x (a,b) = (x >= a) && (x <= b) -- -- verifica se um smbolo um algarismo -- algarismo x = pertence x ('0','9') -- -- Associa uma letra minuscula com a sua -- correspondente maiuscula e mantm o -- smbolo fornecido nos demais casos -- caps x = if minuscula x then chr (ord x - 32) else x -- -- Determina a posio relativa de uma letra -- dentro do alfabeto, onde as maisculas e -- minsculas possuem a mesma posio. -- Outros smbolos devem ser associados ao -- valor 0 (zero) -- ordAlfa x = if letra x then ord (caps x) - 64 else 0 Eis mais alguns exemplos de funes com o tipo char. 123 -- -- verifica se um smbolo uma letra vogal -- +ogal x = letra x && ocorre (caps x) vogais where vogais = ['A','E','I','O','U'] -- o1orre x xs = or [x == y | y<- xs] -- -- verifica se um smbolo uma letra -- consoante -- 1onsoante x = letra x && (not (vogal x)) -- -- -- Descreve uma lista de pares onde o primeiro termo -- um nmero entre 0 e 255 e o segundo o caracter -- correspondente. O intervalo desejado informado -- por um par de valores no intervalo 0 a 255. -- ta?Hr%Ihr (i,f) = [(x, chr x) | x <- [i..f]] Vejamos alguns exemplos de uso das definies acima apresentadas: Main> ta?Hr%Ihr (65,70) [(65,'A'),(66,'B'),(67,'C'),(68,'D'),(69,'E'),(70,'F')] (357 reductions, 604 cells) Main> ta?Hr%Ihr (97,102) [(97,'a'),(98,'b'),(99,'c'),(100,'d'),(101,'e'),(102,'f')] (357 reductions, 607 cells) Main> ta?Hr%Ihr (45,50) [(45,'-'),(46,'.'),(47,'/'),(48,'0'),(49,'1'),(50,'2')] (357 reductions, 604 cells) 15.3. O TIPO STRING: Podemos agrupar tomos do tipo caracter (char) para formar o que denominamos de cadeia de caracteres, usualmente chamadas de "palavra". O mecanismo para agregar o construtor da lista. Podemos por exemplo, escrever em HUGS a lista ['c','i','d','a','n','i','a'] para representar a palavra cidadania# Assim procedendo podemos escrever qualquer texto, uma vez que um texto no nada mais nada menos que uma longa cadeia de smbolos envolvendo letras e sinais de pontuao. A separao de palavras obtida pelo uso de um caracter especial, denominado de espa3o, que tem representao interna igual a 32. Por exemplo, para representar a expresso "Vamos nessa!", usamos a lista: ['V','a','m','o','s',' ','n','e','s','s','a','!']. 124 Uma boa notcia que o HUGS nos proporciona uma maneira mais amistosa para tratar com lista de caracteres. Em HUGS podemos representar uma lista de caracteres, envolvendo a cadeia por aspas duplas, o que por certo, alm de mais elegante e legvel, no poupa trabalho. Por exemplo, a cadeia acima representada por ser tambm escrita na seguinte forma: "Vamos nessa!" uma lista de caracteres o HUGS associa um tipo sinnimo, denominado strin,. Assim, as duas representaes acima so idnticas, conforme podemos observar na avaliao abaixo:
Prelude> "Vamos nessa!" == ['V','a','m','o','s',' ','n','e','s','s','a','!'] True (64 reductions, 102 cells) Um bom lembrete que, cadeias de caracteres so definidas como listas, e como tais, podemos usar sobre elas todas as operaes que sabemos at agora sobre listas. Por exemplo, podemos construir uma funo para contar a quantidade de vogais existente em uma cadeia.
15.4. FUNES BSICAS PARA O TIPO STRING: Uma cadeia de caracteres, por ser uma lista, herda todas as operaes que j apresentamos para as listas. Alm dessas, podemos contar ainda com algumas operaes que apresentamos a seguir: words xs - Associa uma cadeia de caracteres com a lista de pala7ras nela contida. Entende-se por pala7ra um agrupamento de smbolos diferentes do smbolo espa3o (J J)#
Prelude> or%s "Se temos que aprender a fazer, vamos aprender fazendo!" ["Se","temos","que","aprender","a","fazer,", "vamos","aprender","fazendo!"] (2013 reductions, 3038 cells) Podemos observar que os smbolos usuais de separao (por exemplo, "," e "!" so considerados como parte das pala7ras. Vamos agora construir uma funo que considere os smbolos usuais de separao, alm do espao. 125 -- -- A funo pala+ras -- pala+ras xs = [ takeWhile letra x | x <- words xs] -- -- -- ... Main> pala+ras "Se temos que aprender a fazer, vamos aprender fazendo!" ["Se","temos","que","aprender","a","fazer","vamos","aprender","fazendo"] (3869 reductions, 5444 cells) Main> pala+ras "jose123 maria456 joana!!!" ["jose","maria","joana"] (1445 reductions, 2081 cells) Main> or%s "jose123 maria456 joana!!!" ["jose123","maria456","joana!!!"] (951 reductions, 1423 cells)
De fato, a funo paIavras trata a questo proposta: abandonar os smbolos de pontuao. Acontece que ela abandona muito mais que isso, como podemos ver no exemplo, onde a cadeia "jose123" perde o seu sufixo numrico, tornando-se apenas "jose". Vamos construir uma nova funo ento onde isso possa ser resgatado.
-- -- A funo pala+ras1 -- pala+ras1 xs = [ takeWhile alfa x | x <- words xs] where alfa x = letra x || algarismo x -- -- -- ... Main> pala+ras1 "x123 y456 aux!!!" ["x123","y456","aux"] Bom, parece que agora temos uma soluo adequada. Exerccios 1. Verificar se uma letra vogal. 2. Verificar se uma letra consoante. 3. Gerar a lista de pares onde o primeiro termo um nmero entre 0 e 255 e o segundo termo o caracter correspondente pela tabela ASC. O intervalo de valores desejados informado por um par de valores entre 0 e 255. 4. Dadas duas strings xs e ys, verificar se xs prefixo de ys. 5. Dadas duas strings xs e ys, verificar se xs sufixo de ys. 6. Dadas duas strings xs e ys, verificar se xs sublista de ys. 126 16. O PARADIGMA RECURSIVO 127 16.1. INTRODUO: Como j falamos anteriormente, existem vrias maneiras de definir um conceito. A essas maneiras convencionamos chamar de paradigmas. Aqui trataremos de mais um destes, o paradigma recursivo. Dizer que trataremos de mais um simplificar as coisas, na verdade este paradigma um dos mais ricos e importantes para a descrio de computaes. O domnio deste paradigma de fundamental importncia para todo aquele que deseja ser um expert em Programao de Computadores enquanto cincia e tecnologia. De uma maneira simplificada podemos dizer que o ncleo deste paradigma consiste em descrever um conceito de forma recursiva. sto equivale a dizer que definiremos um conceito usando o prprio conceito. Apesar de disto parecer muito intrigante, no se assuste, aos poucos, quando esboarmos melhor a idia ela se mostrar precisa, simples e poderosa. Vamos pensar num conceito bem corriqueiro. Que tal definirmos o conceito escada. Como podemos descrever escada usando a prpria escada? A resposta bem simples: Q!a escada i,ual a u! de,rau se,uido de u!a escada (Figura 15.1). Fcil no ? Ser que isto basta? Onde est o truque? Parece que estamos andando em crculo, no mesmo? Para entender melhor vamos discutir a seguir alguns elementos necessrios para a utilizao correta da recurso na definio de novas funes. 16.2. DESCRIO RECURSIVA DE UM CONCEITO FAMILIAR: Antes de avanar em nossa discusso vamos apresentar mais um exemplo. Desta vez usaremos um que bastante familiar para alunos de cincias exatas. Estamos falando da descrio do fatorial de um nmero. J vimos neste curso uma forma de descrever este conceito dentro do HUGS quando estudamos o paradigma aplicativo. Na oportunidade usamos a seguinte descrio: / fatorial de um n8mero natural n % ! 4 igual ao produto de todos os n8meros naturais de 1 at4 n 128 degrau escada escada Figura 15.1 Uma escada Ou ainda em notao mais formal: n 6 = $ x % x & x ??? x n Em HUGS, como j vimos, teremos a seguinte definio: -- -- definio (aplicativa) de fatorial -- fat n = pro%u1t 31..n5 ... $ fat " 120 $ fat ' 1 $ fat 1 1 H uma outra forma de definir, tambm familiar aos alunos do primeiro ano universitrio: / Aatorial de um n8mero natural n 4 igual ao produto deste n8mero pelo fatorial de seu antecessor Novamente, sendo mais formal, podemos escrever: n 6 = n x (n > $) 6 E em HUGS, como ficaria? Que tal a definio a seguir? -- -- definio recursiva de fatorial -- fat n = n J fat &n - 1) Vamos exercitar a definio: Main> fat " (23967 reductions, 47955 cells) :RRHR: Iontrol sta18 o+erflo Bom, parece que houve um pequeno problema com nossa definio. A avaliao de fat 5 produziu uma situao de erro. Vamos deixar para entender este erro melhor para depois. Por enquanto j podemos adiantar que ele foi provocado por um pequeno esquecimento de nossa parte. 129 Na verdade a nossa definio recursiva para fatorial estava incompleta. Esta que exibimos s se aplica aos naturais maiores que zero. A definio do fatorial de zero no recursiva, ela independente: M fatorial de 1ero i,ual a %# Temos ento duas definies para fatorial e precisamos integr-las. Vejamos uma tentativa: / Aatorial de um n8mero natural n 4) 1. igual a 1 se n=!9 2 igual ao produto deste n8mero pelo fatorial de seu antecessor1 se n % ! Vamos ver como essa integrao pode ser feita em HUGS. Podemos de imediato observar que trata-se de uma definio condicional e logo nos vem a lembrana de que nossa linguagem possui um mecanismo, as expresses condicionais. -- -- definio recursiva de fatorial -- (corrigida) -- fat n = if n==0 then 1 else n * fat (n - 1) Vamos submeter algumas situaes para o HUGS: Main> fat " 120 (79 reductions, 124 cells) Main> fat !' 2432902008176640000 (258 reductions, 466 cells) Main> fat ' 1 (18 reductions, 18 cells) Pelo visto agora deu tudo certo. 16.3. ELEMENTOS DE UMA DESCRIO RECURSIVA: Em uma descrio recursiva devemos ter em conta certo elementos importantes. fundamental que todos eles sejam contemplados para que nossas descries estejam corretas. O exemplo anteriormente apresentado suficiente para ilustrar todos eles. Vamos ento discuti-los: Definio geraI : Toda definio recursiva tem duas partes, uma delas se aplica a um valor qualquer do domnio do problema, denominamos de geral. Esta tem uma caracterstica muito importante, o conceito que est sendo definido deve ser utilizado. Por exemplo, para definir fatorial de n, usamos o fatorial do antecessor de n. Observe aqui, entretanto que o mesmo conceito foi utilizado, mas no para o mesmo valor. Aplicamos o conceito a um valor mais simples, neste caso o antecessor de n. 130 Definio independente : A outra parte da definio destinada ao tratamento de um valor to simples que a sua definio possa ser dada de forma independente. Este elemento tambm conhecido como base da recurso. No caso do fatorial, o valor considerado o zero. Obteno de vaIores mais simpIes : Para aplicar o conceito a um valor mais simples precisamos de uma funo que faa este papel. No caso do fatorial, usamos a subtrao de n por 1, obtendo assim o antecessor de n. Em cada caso, dependendo do domnio do problema e do problema em si, precisaremos encontrar a funo apropriada. Funo auxiIiar : Na definio geral, para obter um valor usando o valor considerado e o valor definido recursivamente, em geral faz-se necessrio o uso de uma funo auxiliar. Algumas vezes esta funo pode ser originada a partir de um conceito aplicvel a dois elementos e que desejamos estender aos elementos de uma lista. Um exemplo o caso da somatria dos elementos de uma lista, como veremos adiante. No caso do fatorial esta funo a multiplicao. Garantia de atingir o vaIor independente : fundamental que a aplicao sucessiva da funo que obtm valores mais simples garanta a determinao do valor mais simples. Este valor tambm denominado de base da recurso. Por exemplo, no caso do fatorial, sabemos que aplicando a subtrao sucessivas vezes produziremos a seqncia: n, (n-1), (n-2), ... 0 /sta condi34o funda!ental para ,arantir que ao a$aliar!os u!a express4o atin,ire!os a +ase da recurs4o# Voltemos definio do fatorial para destacarmos os elementos acima citados, como podemos observar no quadro esquemtico a seguir: 131 16.4. AVALIANDO EXPRESSES: A esta altura dos acontecimentos a curiosidade sobre como avaliar expresses usando conceitos definidos recursivamente j deve estar bastante aguada. No vamos, portanto retardar mais essa discusso. Apresentamos a seguir um modelo bastante simples para que possamos entender como avaliar expresses que usam conceitos definidos recursivamente. Novamente no precisaremos entender do funcionamento interno de um computador nem da maneira como uma determinada implementao de HUGS foi realizada. Basta-nos o conceito de reduo que j apresentamos anteriormente. Relembremos o conceito de reduo. O avaliador deve realizar uma seqncia de passos substituindo uma expresso por sua definio, at que se atinja as definies primitivas e os valores possam ser computados diretamente. Vamos aplicar ento este processo para realizar a avaliao da expresso fat 5 passo Reduo Justificativa 0fat 5 expresso proposta 15 * fat 4 substituindo fat por sua definio geral 25*(4 * fat 3) dem 35*(4* (3 * fat 2)) dem 45*(4*(3*(2 * fat 1))) dem 55*(4*(3*(2*(1 * fat 0) dem 65*(4*(3*(2*(1 * 1)))) usando a definio especfica 75*(4*(3*(2*1))) usando a primitiva de multiplicao 85*(4*(3*2) dem 95*(4*6) dem 105 * 24 dem 11120 dem 132 Surpreso(a)? Simples, no? assim mesmo, bem simples. A cada passo vamos substituindo uma expresso por outra at que nada mais possa ser substitudo. O resultado surgir naturalmente. Mais tarde voltaremos ao assunto. 16.5. RECURSO EM LISTAS: A esta altura deste curso j estamos certos que o uso de lista indispensvel para escrever programas interessantes. Em vista disso, nada mais bvio que perguntar sobre o uso de recurso em listas. Veremos que o uso de definies recursivas em listas produz descries simples, precisas e elegantes. J est na hora de alertar que os valores sobre os quais aplicamos os conceitos que queremos definir recursivamente possuem uma caracterstica importantssima, eles em si so recursivos. Por exemplo, qualquer valor pertencente aos naturais pode ser descrito a partir da existncia do zero e da funo sucessor (suc). Vejamos como podemos obter o valor 5: 5 ( suc(suc(suc(suc(suc -))))) As listas so valores recursivos. Podemos descrever uma lista da seguinte maneira: Uma lista : 1. a lista vazia; 2. um elemento seguido de uma lista Esta natureza recursiva das listas nos oferece uma oportunidade para, com certa facilidade, escrever definies recursivas. A tcnica consiste basicamente em: 1. Obter a definio geral: isto consiste em identificar uma operao binria simples que possa ser aplicada a dois valores. O primeiro deles o primeiro (head) da lista e o outro um valor obtido pela aplicao do conceito em definio ao resto (taiI) da lista; 2. Obter a definio independente, que se aplicar base da recurso. Esta, em geral, a lista $a1ia; 3. Garantir que a aplicao sucessiva do taiI levar base da recurso. Na Figura 15.2 ilustramos o processo recursivo de obter listas cada vez menores, atravs da aplicao da funo taiI. Ao final do processo obteremos a lista vazia ([ ]). 133 ExempIo 01 - Descrever o somatrio dos elementos de uma lista. SoIuo - Podemos pensar da seguinte maneira: o somatrio dos elementos de uma lista igual soma do primeiro elemento da lista como o somatrio do resto da lista. Alm disso, o somatrio dos elementos de uma lista vazia igual a zero. Vejamos ento a codificao: -- -- definio recursiva da somatria dos -- elementos de uma lista -- somat ,s = if null ,s then ' else hea% ,s 9 somat &tail ,s) [x o , x 1 , x 2 , x 3 , x 4 ... x n-1 ] x o [x 1 , x 2 , x 3 , x 4 ... x n-1 ] taiI x 1 [x 2 , x 3 , x 4 ... x n-1 ] x 2 [x 3 , x 4 ... x n-1 ] x 3 [x 4 ... x n-1 ] x 4 [... x n-1 ] x n-1 [ ]
... Figura 15.2 - componentes recursivos de uma Iista head 134 x o + somat [x 1 , x 2 , x 3 , x 4 ... x n-1 ] X 0 + x 1 + somat [x 2 , x 3 , x 4 ... x n-1 ] ... X 0 + x 1 + x 2 + somat [x 3 , x 4 ... x n-1 ] X 0 + x 1 + x 2 + x 3 + somat [x 4 ... x n-1 ] X 0 + x 1 + x 2 + x 3 + somat [x 4 ... x n-1 ] X 0 + x 1 + x 2 + x 3 + x 4 + somat [... x n-1 ] X 0 + x 1 + x 2 + x 3 + x 4 + ... + x n-1 + somat [ ] somat [x o , x 1 , x 2 , x 3 , x 4 ... x n-1 ] Figura 15.2 desenvolvimento da computao de somatrio dos elementos de uma lista X 0 + x 1 + x 2 + x 3 + x 4 + ... + x n-1 + 0 A seguir alguns exemplos de uso: Main> somat [4,5,2,7,9] 27 (52 reductions, 60 cells) Main> somat [1..10] 55 (275 reductions, 421 cells) Main> somat [1000,9999..1] 0 (45 reductions, 68 cells) Main> somat [1000,999..1] 500500 (18051 reductions, 25121 cells) ExempIo 02 - Descrever a funo que determina o elemento de valor mximo uma lista de nmeros. SoIuo: O mximo de uma lista o maior entre o primeiro elemento da lista e o mximo aplicado ao resto da lista. Uma lista que tem apenas um elemento tem como valor mximo o prprio elemento (Figura 15.3). A definio recursiva apresentada a seguir: -- -- definio recursiva do mximo de uma lista -- ma,imo ,s = if null &tail ,s) then hea% ,s else maior &hea% ,s) &ma,imo &tail ,s)) -- maior , 7 = if , > 7 then , else 7 E vamos acompanhar agora algumas submisses: Main> ma,imo 34(6(#(.0(3!(4"(0.(6"(315 98 (126 reductions, 150 cells) Main> ma,imo &31..1'''59931"''(14''..15) 135 maximo [x o , x 1 , x 2 , x 3 , x 4 ... x n-1 ] maior( x o , maximo [x 1 , x 2 , x 3 , x 4 ... x n-1 ] ) maior( x o , maior(x 1 , maximo [x 2 , x 3 , x 4 ... x n-1 ])) )) maior( x o , maior(x 1 , maior(x 2, maximo [x 3 , x 4 ... x n-1 ] ))) maior( x o , maior(x 1 , maior(x 2, maior(x 3, maximo [x 4 ... x n-1 ] )))) maior( x o , maior(x 1 , maior(x 2, maior(x 3, maior(x 4 , ... maximo[x n-2 , x n-1 ]))))) maior( x o , maior(x 1 , maior(x 2, maior(x 3, maior(x 4 , ... maior(x n-2 , maximo[ x n-1 ]))))) maior( x o , maior(x 1 , maior(x 2, maior(x 3, maior(x 4 , ... maior(x n-2 , x n-1 )))))) Figura 15.3 desenvolvimento da computao do elemento mximo de uma lista 1500 (31419 reductions, 44567 cells) Main> ma,imo 31''(&1'' - 8) .. 15 here 8 = .' 100 (91 reductions, 125 cells) E agora uma surpresa. Main> ma,imo 3 5 ERROR: Unresolved overloading *** Type : Ord a => a *** Expression : maximo [] Voc consegue explicar? ExempIo 03 - Descrever a funo que verifica se um dado valor ocorre em uma lista tambm dada. SoIuo : Podemos pensar da seguinte maneira: Um dado elemento k ocorre em uma lista se ele igual ao primeiro elemento da lista ou se ele ocorre no resto da lista. Em uma lista vazia no ocorrem elementos quaisquer (Figura 15.4). Vejamos ento a codificao: -- -- descreve a ocorrncia de dado k em uma lista xs -- o1orre 8 ,s = if null ,s then False else &8==hea%&,s)) 44 o1orre 8 &tail ,s) E algumas submisses: 136 ocorre k [x o , x 1 , x 2 , x 3 , x 4 ... x n-1 ] k = x o | ocorre k [x 1 , x 2 , x 3 , x 4 ... x n-1 ] k = x o | (k = x 1 | ocorre k [x 2 , x 3 , x 4 ... x n-1 ]) k = x o | (k = x 1 | ( k = x 2 | ocorre k [x 3 , x 4 ... x n-1 ])) k = x o | (k = x 1 | ( k = x 2 | (k = x 3 | ocorre k [x 4 ... x n-1 ]))) k = x o | (k = x 1 | ( k = x 2 | (k = x 3 | (k = x 4 ) || ocorre k [... x n-1 ])))) ... k = x o | (k = x 1 | ( k = x 2 | (k = x 3 | (k = x 4 ) | ... ocorre k [x n-1 ]))))) k = x o | (k = x 1 | ( k = x 2 | (k = x 3 | (k = x 4 ) | ... | (k = x n-1 )| ocorre k [ ]))))) Figura 15.4 desenvolvimento da computao da ocorrncia de um elemento em uma lista Main> o1orre 5 [8,65,46,23,99,35] False (71 reductions, 111 cells) Main> o1orre 5 [8,65,46,5,23,99,35] True (47 reductions, 58 cells) Main> o1orre 5 [ ] False (16 reductions, 30 cells) ExempIo 04 - Descrever a funo que obtm de uma lista xs a sublista formada pelos elementos que so menores que um dado k : SoIuo : Precisamos descrever uma nova lista, vamos denomin-la de !enores, em funo de xs e de k. Quem ser esta nova lista? Se o primeiro elemento de xs for menor que k, ento ele participar da nova lista, que pode ser descrita como sendo formada pelo primeiro elemento de xs seguido dos menores que k no resto de xs. Se por outro lado o primeiro no menor que k, podemos dizer que a lista resultante obtida pela aplicao de menores ao resto da lista. Novamente a base da recurso definida pela lista vazia, visto que em uma lista vazia no ocorrem elementos menores que qualquer k. A codificao apresentada a seguir: -- -- define a lista de menores que um dado elemento em -- uma lista dada -- menores 8 ,s = if null ,s then ,s else if hea% ,s 2 8 then hea% ,s : menores 8 &tail ,s) else menores 8 &tail ,s) Desta podemos obter as seguintes avaliaes: Main> menores 23 [8,65,46,5,23,99,35] [8,5] (122 reductions, 188 cells) Main> menores 46 [8,65,46,5,23,99,35] [8,5,23,35] (135 reductions, 175 cells) Main> menores 5 [] [] (17 reductions, 24 cells) 16.6. EXPLORANDO REUSO: Segundo o Professor George Polya, aps concluir a soluo de um problema, devemos levantar questionamentos a respeito das possibilidades de generalizao da soluo obtida. Dentro deste esprito, vamos explorar um pouco a soluo obtida para o problema descrito a seguir. ExempIo 5 (Sub-Iista de nmeros pares): Dada uma lista xs, desejamos descrever uma sublista de xs formada apenas pelos nmeros pares existentes em xs. 137 SoIuo: Devemos considerar a existncia de suas situaes, como no problema de encontrar a sublista dos menores (Exemplo 4): 1. O primeiro elemento da lista um nmero par, neste caso a sublista resultante dada pela juno do primeiro com a sublista de pares existente no resto da lista. 2. O primeiro no par. Neste caso a sublista de pares em xs obtida pela seleo dos elementos pares do resto de xs. Concluindo, tomemos como base da recurso a lista vazia, que obviamente no contm qualquer nmero. Eis a soluo em HUGS:
-- -- sublista de nmeros pares -- slpares ,s = if null ,s then ,s else if e+en &hea% ,s) then hea% ,s : slpares &tail ,s) else slpares &tail ,s) E a avaliao de algumas instncias: Main> slpares [1..10] 3!(4(6(.(1'5 (322 reductions, 438 cells) Main> slpares [1,3..100] 35 (962 reductions, 1183 cells) Vamos agora, seguindo as orientaes do mestre Polya, buscar oportunidades de generalizao para esta funo. Podemos fazer algumas perguntas do tipo: 1. Como faria uma funo para determinar a sublista dos nmeros mpares a partir de uma dada lista? 2. E se quisssemos a sublista dos primos? 3. E que tal a sublista dos mltiplos de cinco?
Uma breve inspeo na soluo acima nos levaria a entender que a nica diferena entre as novas funes e a que j temos a funo que verifica se o primeiro elemento satisfaz uma propriedade, no caso presente a de ser um nmero par (even), conforme destacamos a seguir:
138 -- -- sublista de nmeros pares -- slpares ,s = if null ,s then ,s else if e+en &hea% ,s) then hea% ,s : slpares &tail ,s) else slpares &tail ,s) -- -- sublista de nmeros impares -- slimpar ,s = if null ,s then ,s else if o%% &hea% ,s) then hea% ,s : slimpar &tail ,s) else slimpar &tail ,s) -- -- sublista de nmeros primos -- slprimo ,s = if null ,s then ,s else if primo &hea% ,s) then hea% ,s : slprimo &tail ,s) else slprimo &tail ,s) sto nos sugere que a funo avaliadora pode ser um parmetro. Pois bem, troquemos ento o nome da funo por um nome mais geral e adicionemos sua interface mais uma parmetro. Este parmetro, como sabemos, dever ser do tipo:
alfa -> Doolean Vejamos ento o resultado da codificao, onde a propriedade a ser avaliada se converte em um parmetro: -- -- sublista de elementos de xs que satisfazem -- a propriedade prop -- su?lista prop ,s = if null ,s then ,s else if prop &hea% ,s) then hea% ,s : su?lista prop &tail ,s) else su?lista prop &tail ,s) Vejamos ento algumas aplicaes na nossa funo genrica para determinar sublistas: 139 Main> sublista e+en [1..10] [2,4,6,8,10] Main> sublista o%% [1..10] [1,3,5,7,9] Main> sublista &2") [1..10] [1,2,3,4] Main> sublista &>=") [1..10] [5,6,7,8,9,10] Observe que a funo que havamos anteriormente definido para determinar os elementos menores que um determinado valor k, da mesma forma que a funo para determinar os maiores que k, est contemplada com a nossa generalizao. As duas ltimas avaliaes no quadro acima ilustram a determinao da sublista dos valores menores que 5 e a dos maiores ou iguais a 5. Exerccios: Descreva funes em HUGS que utilizem recurso para resolver os problemas abaixo. 1. Obter a interseo de duas listas xs e ys. 2. Dadas duas strings xs e ys, verificar se xs prefixo de ys. 3. Dadas duas strings xs e ys, verificar se xs sufixo de ys. 4. Dadas duas strings xs e ys, verificar se xs sublista de ys. 5. inverter uma lista xs; 6. Definir a funo tWhile, que tenha o mesmo comportamento que a funo takeWhile. 7. Definir a funo dWhile, que tenha o mesmo comportamento que a funo dropWhile. 8. Verificar se uma string um palndrome (a string a mesma quando lida da esquerda para a direita ou da direita para a esquerda). 9. Verifique se uma string uma palavra. Defina uma palavra como formada apenas por letras. 10. Verificar se os elementos de uma lista so distintos. 11. Determinar a posio de um elemento x em uma lista xs, se ele ocorre na lista. 12. Descrever a lista das palavras que existem no texto, dado um texto. 13. Dadas duas listas xs e ys, ordenadas em ordem crescente, obter a lista ordenada resultante da intercalao de xs e ys. 14. Calcular a combinao de uma lista xs, p a p. 140 17. ORDENAO RECURSVA DE DADOS, ALGUNS PROBLEMAS CORRELATOS E ALGUMAS TCNCAS DE PROPSTO GERAL 17.1 - INTRODUO: Voltemos ento ao problema de ordenar os elementos de uma lista, para o qual j discutimos uma soluo no paradigma aplicativo. =ada u!a lista xs deseja!os descre$er sua ordena34o# Vamos comear propondo e resolvendo um problema mais simples. Insero ordenada: Dada uma lista ordenada xs e um elemento k desejamos descrever uma lista a partir de xs, na qual esteja includo o valor k, com a condio de que a nova lista tambm esteja ordenada. SoIuo : Voltemos a nossa estratgia para obter solues recursivas. Aqui tambm temos dois casos: 1. O valor k menor que o primeiro da lista xs, neste caso a lista resultante descrita pela juno de k com a lista xs; 2. O valor k maior ou igual ao primeiro elemento da lista xs, neste caso a lista resultante descrita pela juno do primeiro da lista xs com a lista obtida pela insero ordenada de k no resto da lista xs. A codificao pode ser realizada da seguinte forma: -- -- insero ordenada de um valor k em uma -- lista ordenada (no decrescente) -- insord k xs = if null xs then [k] else if k < head xs then k : xs else head xs : (insord k (tail xs)) 141 k < x 0 k : Xs Caso contrrio x 0 : insord k
Xs insord k Xs = Figura 17.1 insero ordenada de um elemento k em uma lista Xs E a seguir algumas aplicaes da soluo: Main> insor% 5 [0,2..10] 3'(!(4("(6(.(1'5 (230 reductions, 407 cells) Main> insor% 5 [10,15..50] 3"(1'(1"(!'(!"(3'(3"(4'(4"("'5 (248 reductions, 379 cells) Main> insor% 5 [-10,15..0] 3-1'("5 (92 reductions, 135 cells) Main> insor% 5 [-10,-5..0] 3-1'(-"('("5 (154 reductions, 220 cells) Main> insor% 5 [] 3"5 (23 reductions, 32 cells) Agora j podemos voltar ao nosso problema inicial de ordenao de listas. Vamos em busca de uma primeira soluo: SoIuo : A ordenao no decrescente de uma lista xs qualquer igual insero ordenada do primeiro da lista na ordenao do resto da lista. -- -- ordenao de uma lista -- ordena xs = if null xs then xs else insord (head xs) (ordena (tail xs)) Vejamos a aplicao da soluo algumas instncias: Main> or%ena [3, 4, 50,30,20,34,15] 33(4(1"(!'(3'(34("'5 (241 reductions, 330 cells) Main> or%ena [100,93..50] 3"1(".(6"(#!(#0(.6(03(1''5 (568 reductions, 780 cells) 17.2 DIVISO E CONQUISTA (UMA TCNICA PODEROSA): Alguns problemas possuem solues mais facilmente descritas, algumas at mais eficientes, quando quebramos o problema em partes menores, descrevemos a soluo de cada parte e depois combinamos as solues parciais para obter a soluo completa. Este mtodo denominado de "diviso e conquista". Basicamente buscamos encontrar instncias do problema onde a soluo seja imediata. . Nesta seo veremos alguns exemplos desta abordagem. O primeiro deles, a pesquisa binria, trata da busca de um elemento em uma lista ordenada. Os outros dois, mergesort e quicksort, apresentam solues alternativas para a ordenao de uma lista. 142 17.2.1. PESQUISA BINRIA - Voltemos a um problema j apresentado anteriormente, verificao da ocorrncia de um elemento a uma lista. Segundo a definio que apresentamos para a funo ocorre, apresentada no exemplo 3 do Captulo 16. Podemos constatar que para avaliar expresses onde o elemento procurado no ocorre na lista, o avaliador de expresses precisar fazer uma quantidade de comparaes igual ao comprimento da lista considerada. Na mdia de um conjunto de avaliaes, considerando as avaliaes de expresses em que o elemento procurado est na lista, e que a cada vez estaremos procurando por um elemento distinto, teremos um nmero mdio de comparaes da ordem de (n / 2). Se n for muito grande ficaremos assustados com o nmero de comparaes. Por exemplo, para uma lista de 1000000 (um milho) de elementos, em mdia teremos que fazer 500 mil comparaes. Se pudermos garantir que a lista est ordenada, ento podemos fazer uso de uma estratgia j discutida anteriormente para reduzir este nmero. Estamos falando da rvore binria de pesquisa. A estratgia que usaremos consiste em, a cada passo de reduo, abandonarmos metade da lista considerada a partir da comparao de k com o elemento que se encontra na metade da lista. Se o elemento buscado (k) for igual ao elemento central, ento o processo de avaliao est encerrado. Quando isto no ocorre, devemos ento escolher em qual lista devemos procur-lo. Quando ele menor que o elemento central devemos busc-lo na sublista que antecede o central, caso contrrio devemos busc-lo na sublista dos seus sucessores. Novamente a base da recurso determinada pela lista vazia. Nesta abordagem, a cada escolha abandonamos metade da lista restante. Desta forma, o nmero de comparaes dado pelo tamanho da seqncia:
n@$, n@%, n@B, ??? , n@n Para simplificar a anlise podemos escolher um n que seja potncia de 2. Neste caso podemos assegurar que o comprimento da seqncia dado por:
To( n na base % Voltando ento ao nmero de comparaes necessrias para localizar um elemento, podemos constatar que em uma lista com 1 milho de elementos, ao invs das 500 mil comparaes da soluo anterior, precisaremos no pior caso, de apenas 20 comparaes. sso mesmo, apenas 20. Vamos ento codificao em HUGS:
-- -- pesquisa binria -- pesqbin k xs = if null xs then False else if k == pivot then True else if k < pivot then pesqbin k menores else pesqbin k maiores where p = div (length xs) 2 menores = take p xs maiores = tail (drop p xs) 143 pivot = head (drop p xs) E a seguir, a avaliao de algumas instncias: Main> o1orre 1023 [0..1023] True (24592 reductions, 32797 cells) Main> pesq?in 1023 [0..1023] True (71563 reductions, 92060 cells) 17.2.2 MERGESORT - Existem outras maneiras de se descrever a ordenao de uma lista. Uma delas, denominada !er,esort, se baseia na intercalao de duas listas j ordenadas. Comecemos ento por discutir a intercalao que, em si mesmo, j representa uma ferramenta intelectual bastante interessante. IntercaIao: Antes de ver o mergesort podemos apresentar uma verso recursiva para a intercalao de duas listas em ordem no decrescente. SoIuo: A intercalao de duas listas ordenadas xs e s pode ser descrita atravs de dois casos: 1. se o primeiro elemento de xs menor que o primeiro elemento de s ento a intercalao dada pela juno do primeiro elemento de xs com a intercalao do resto de xs com s; 2. caso contrrio, a intercalao descrita pela juno do primeiro elemento de s com a intercalao do resto de s com xs; A codificao resultante pode ser observada a seguir: -- -- Intercala duas listas em ordem no decrescente -- intercala xs ys = if (null xs) || (null ys) then xs ++ ys else if head xs <= head ys then head xs : intercala (tail xs) ys else head ys : intercala xs (tail ys) E a seguir, algumas submisses e avaliaes do HUGS: Main> inter1ala [1,3..10] [0,2..10] 3'(1(!(3(4("(6(#(.(0(1'5 Main> inter1ala [0,2..10] [1,3..10] 3'(1(!(3(4("(6(#(.(0(1'5 Main> inter1ala [0,2..10] [] [0,2,4,6,8,10] Main> inter1ala [] [] ERROR: Unresolved overloading 144 *** Type : Ord a => [a] *** Expression : intercala [] [] Main> inter1ala [] [0,2..10] 3'(!(4(6(.(1'5 Main> inter1ala [0,2..10] [0,2..10] 3'('(!(!(4(4(6(6(.(.(1'(1'5 Main> inter1ala [9,7..1] [10,8..1] 30(#("(3(1(1'(.(6(4(!5 (o que houve que no ficou ordenada?) Voltemos ao mergesort, ou, em bom portugus, ordenao por intercalao. SoIuo : A ordenao de uma lista por mergesort igual intercalao do mergesort da primeira metade da lista com o mergesort da segunda metade. Esta soluo explora a noo de rvore binria. Neste caso, a lista original dividida em 2 partes, cada uma delas em outras duas e assim sucessivamente at que esta quebra no seja mais possvel. A figura Fig. 17.1 ilustra o processamento da ordenao de uma lista.
145 Vejamos ento como fica a codificao em HUGS. -- -- ordena uma lista pela intercalao da ordenao de -- suas duas metades -- mergesort xs = if null (tail xs) then xs else intercala (mergesort m) (mergesort n) where m = take k xs n = drop k xs k = div (length xs) 2 Main> mergesort [1..10] [1,2,3,4,5,6,7,8,9,10] (1593 reductions, 2185 cells) Main> mergesort [10,9..1] [1,2,3,4,5,6,7,8,9,10] (1641 reductions, 2236 cells) 17.2.3. QUICKSORT - Existe uma maneira muito famosa de resolver o mesmo problema de ordenao, usando ainda a noo de diviso e conquista, muito parecida com o mergesort. mplementaes desta soluo reduzem sensivelmente o nmero de comparaes necessrias e so, portanto muito utilizadas. SoIuo : Na verso usando o mergesort dividamos a instncia original exatamente ao meio. Nesta vamos dividir tambm em duas, mas com seguinte critrio: a primeira com os elementos menores que um elemento qualquer da lista e a segunda com os elementos maiores ou iguais a ele. Este elemento denominado pi$ot e existem vrias formas de escolh-lo. A melhor escolha aquela que produzir as sublistas com comprimentos bem prximos, o que repercutir no desempenho da avaliao. Aqui nos limitaremos a escolher como pivot o primeiro elemento da lista. Assim sendo, aps obter a ordenao das duas listas, basta juntar a ordenao da primeira, com o pivot e finalmente com a ordenao da segunda. A figura Fig. 9.2 ilustra a aplicao do quicksort a uma instncia do problema. 146 E a seguir vejamos a codificao. quicksort xs = if (null xs) || (null (tail xs)) then xs else quicksort (sublista (< pivot) (tail xs)) ++ [pivot] ++ quicksort (sublista (>= pivot) (tail xs)) where pivot = head xs Convidamos o leitor a apreciar e discutir a elegncia, a compacidade e a clareza da descrio do quicksort. Vejamos a avaliao de algumas instncias: Main> qui18sort [4,5,6,7,8,3,2,1] [1,2,3,4,5,6,7,8] (595 reductions, 755 cells) Main> qui18sort [1..10] [1,2,3,4,5,6,7,8,9,10] (1536 reductions, 1881 cells) Main> qui18sort [10,9..1] [1,2,3,4,5,6,7,8,9,10] (1541 reductions, 1952 cells) 147 Figura 17.1 desenvolvimento da computao da funo palndromo Main> mergesort [10,9..1] [1,2,3,4,5,6,7,8,9,10] (1647 reductions, 2283 cells) Main> mergesort ,s == qui18sort ,s where xs = [1..10] True (2805 reductions, 3563 cells) Main> mergesort [2,14,16,23,29,35,47,68,70,90] [2,14,16,23,29,35,47,68,70,90] (1414 reductions, 1922 cells) Main> qui18sort [2,14,16,23,29,35,47,68,70,90] [2,14,16,23,29,35,47,68,70,90] (1357 reductions, 1618 cells) Main> or%ena [2,14,16,23,29,35,47,68,70,90] [2,14,16,23,29,35,47,68,70,90] (236 reductions, 336 cells) 17.3. PROCESSAMENTO DE CADEIAS DE CARACTERES: As cadeias de caracteres, como j vimos, tambm so listas, portanto o uso de recurso com cadeias segue as mesma recomendaes. Para ilustrar vamos apresentar alguns exemplos. ExempIo 01 - [Palndromo] Dada uma cadeia de caracteres verifique se um palndromo. Segundo o dicionrio, um palndromo uma frase ou palavra, que no importando o sentido que se l, significa a mesma coisa. Por exemplo, "Socorram-me subi no nibus em Marrocos". Vejam que a quantidade de espaos, os separadores e os trminos de palavra no so considerados. Aqui vamos tratar a questo de forma simplificada, os separadores sero tratados como caracteres comuns. SoIuo : Neste caso, importante observar que podemos olhar a cadeia como sendo formada por pares de valores eqidistantes dos extremos. Um cadeia palndromo se os seus extremos so iguais e o meio da lista um palndromo. A base da recurso so as cadeias vazias ou aquelas com apenas um elemento. Vejamos ento a codificao em HUGS e a avaliao para algumas instncias.
E agora uma avaliao de algumas listas candidatas a palndromo: x 0 =
x n-1 & palndromo [x 1 , x 2 , x 3 , x 4 , x 5 , ..., ] x 0 =
& (x 1 = x n-1 &
palndromo [ x 2 , x 3 , x 4 , x 5 , ..., ] ) x 0 =
& (x 1 = x n-1 &
( X 2 = X n-2 & palndromo [x 3 , x 4 , x 5 , ..., ] )) palndromo [x 0 , x 1 , x 2 , x 3 , x 4 , x 5 , ..., x n-1 ] 148 -- -- Verifica se uma sentena Palndromo -- -- palindromo xs = if null xs || null (tail xs) then True else (head xs == last xs) && palindromo (meio xs) where meio xs = init (tail xs) Seguindo nosso padro de apreentao,vejamos como ficam algumas avaliaes: Main> palin%romo "socorrammesubinoonibusemmarrocos" *rue (687 reductions, 698 cells) Main> palin%romo "socorram-me subi no onibus em marrocos" False (891 reductions, 907 cells) Main> palin%romo "ama" *rue (31 reductions, 43 cells) Main> palin%romo "papagaio" False (29 reductions, 45 cells) Main> palin%romo "arara" *rue (49 reductions, 61 cells) ExempIo 02 - [Prefixo] Dadas duas cadeias de caracteres verifique se a primeira idntica subcadeia formada pelos primeiros caracteres da segunda. Por exemplo, "aba" prefixo da cadeia "abacaxi" e "pre" prefixo de "prefixo". SoIuo : De imediato podemos dizer que uma cadeia xs prefixo de uma cadeia ys quando: iii) o primeiro elemento de xs igual ao primeiro elemento de ys e; iv) o restante de xs prefixo do restante de ys. v) Quanto base da recurso, temos que considerar duas situaes: i) A primeira tem como base que a cadeia vazia prefixo de qualquer outra cadeia; ii) A segunda leva em conta que nenhuma cadeia pode ser prefixo de uma cadeia vazia (exceto a cadeia vazia). Vejamos como fica em Haskell: 149 -- -- Verifica se uma cadeia xs prefixo -- de uma segunda (ys) -- prefixo xs ys = if null xs then True else if null ys then False else (head xs == head ys) && prefixo (tail xs) (tail ys) As avaliaes de expresses a seguir nos permitem observar o funcionamento de nossa descrio: Main> prefi,o "aba" "abacadraba" True Main> prefi,o "" "abacadraba" True Main> prefi,o "pre" "prefixo" True Main> prefi,o "prefixo" "pre" False Main> prefi,o "prefixo" "" False
ExempIo 03 - [Casamento de Padro] Verificar se uma cadeia satisfaz um determinado padro um processamento muito til e constantemente realizado na prtica da computao. Aqui nos ateremos a uma forma simplificada deste problema que consiste em verificar se uma cadeia su+cadeia de outra. SoIuo : Uma rpida inspeo nos leva constatao de que o problema anterior parecido com este, exceto pelo fato de que a primeira cadeia pode ocorrer em qualquer lugar da segunda. Podemos dizer ento que a primeira cadeia ocorre na segunda se ela um prefixo da primeira ou se ela ocorre no resto da segunda. Vejamos como fica em HUGS: -- -- Verifica se uma cadeia xs subcadeia -- de uma outra (ys) -- subcadeia xs ys = if null ys || null (tail ys) then False else prefixo xs ys || subcadeia xs (tail ys) A avaliao das expresses a seguir ajuda no entendimento: 150 Main> su?1a%eia "" "prefacio" *rue Main> su?1a%eia "pre" "prefacio" *rue Main> su?1a%eia "cio" "prefacio" *rue Main> su?1a%eia "efa" "prefacio" *rue Main> su?1a%eia "acido" "prefacio" False Main> su?1a%eia "efa" "" False Exerccios: 1. Pesquise como descrito o mtodo da "bolha para ordenao. Faa uma funo em Haskell para ordenar uma lista pelo mtodo da bolha. 151 18. APLICAES Neste captulo apresentamos uma srie temtica de exerccios, buscando dar ao leitor uma viso mais ampla das possibilidades de uso da programao funcional. A inteno apresentar vrios contextos onde precisamos programar computadores. Em todos, o contexto descrito e vrios exerccios so apresentados. Aos professores e estudantes, sugerimos que o contexto sirva de pretexto para a formulao e resoluo de novos exerccios. Comeamos pelo Domin Bar, onde, buscamos na ludicidade do jogo, apresentar as necessidades de manipular informao. Na seqncia apresentamos uma srie de problemas considerando as necessidades de informao de um Banco de Sangue, o manuseio de mensagens de um Correio EIetrnico, a organizao de uma Lista de Compras, um sistema de Passagens Areas, Gerncia Acadmica, Agncia de Turismo e exerccios sobre EspetcuIos Teatrais. 18.1 O DOMIN BAR: O domin de nmeros uma coleo de pedras, utilizado na maioria das vezes como um excelente passatempo. Das tantas formas de utilizar o domin, destacamos uma, utilizada no Amazonas, principalmente em Manaus, mas tambm em muitas praias pelo mundo afora onde existirem amazonenses, em particular nas praias de Fortaleza. Os Amazonenses costumam cham-la de "domin bar", em homenagem a uma tribo que habitava a regio onde foi fundada a cidade de Manaus. A maioria dos exerccios deste captulo foram desenvolvidos em Manaus, no final da dcada de 80. De l pra c, vrios outros foram acrescentados, mas a lista como um todo permanece indita. A inteno desta seo apresentar alguns poucos exerccios resolvidos e propor outros. A idia no desenvolver o jogo e sim, inspirar-se em situaes do jogo para propor exerccios interessantes e desafiadores. Deixamos o desenvolvimento do jogo completo como sugesto para o trabalho em grupos. Ao final do captulo discutiremos um pouco sobre a programao do jogo. PreIiminares: O material do jogo um conjunto formado por 28 "peas", cada uma delas com duas "pontas". Cada "ponta" representa um valor de 0 (zero) a seis (6), perfazendo portanto 7 valores diferentes. Cada valor possui um nome prprio: o Zero chama-se "branco", o um chama-se "s", o dois o "duque", o trs chama-se "terno", o quatro a "quadra", o cinco a "quina" e o seis denomina-se "sena". O nome de uma "pedra" dado pelo nome de suas "pontas", por exemplo, "quina e terno", o nome da "pedra" que possui em uma ponta o valor 5 e na outra o valor 3. As pedras que possuem o mesmo valor nas duas pontas so denominadas de "carroa". Para cada tipo de valor existem 7 pedras, por exemplo, para o "terno" teremos: terno e branco, terno e s, terno e duque, carroa de terno, quadra e terno, quina e terno, sena e terno. O jogo , em geral, disputado por duplas, ganhando a que fizer o maior numero de pontos, a partir de um mnimo pr- estabelecido. A seguir apresentamos em detalhes os vrios elementos do jogo. Peas do Jogo: Os elementos do jogo so (28) vinte e oito peas, cada uma com duas pontas, na qual marcado um valor que varia de 0 a 6. Para jogar, as "pedras" so embaralhadas e escolhidas pelos jogadores. A cada jogador cabem 7 pedras. Com o desenrolar do jogo a quantidade de pedras vai sendo decrescida, at que, eventualmente chegue em zero. 152 Participantes: duas duplas (eventualmente pode ser jogado individualmente, com 2, 3 ou 4 jogadores). Objetivo: atingir um total mnimo de 200 pontos. Vence o jogo a dupla que ao final de uma rodada tiver o maior nmero de pontos. Dinmica: o jogo se desenvolve em uma quantidade qualquer de eventos menores denominados de rodada. A figura 18.1 ilustra um instante de jogo. Rodada: em uma rodada, um aps o outro, no sentido horrio, os jogadores vo fazendo suas jogadas, combinando suas pedras de domin com a "figura que j est formada na mesa de jogo. Pontuao: existem 4 formas para obter pontos: 1. Durante o jogo, a figura formada na mesa possui 1 (quando existe apenas uma pea assentada), 2, 3 ou 4 pontas. soma dos valores dessas pontas denomina-se de: "os pontos da mesa. Quando essa soma produz um mltiplo de 5, o jogador que sentou a ltima pedra pode requerer que eles sejam anotados em favor de sua Figura 18.1 um instante do jogo de domin, com quatro pontas abertas: terno, s, duque e quadra. 153 dupla. Veja que s o jogador que sentou a pedra pode reivindicar os pontos e isto tem que ocorrer antes que o prximo jogador sente a sua pedra; 2. Quando um jogador no possui pedra para colocar na mesa (ou seja, uma que combine com uma das pontas), ele passa a vez, e a dupla adversria ganha 10 pontos. Se um jogador percebe que com a colocao de sua pea ele conseguir fazer com que todos os demais passem, inclusive o seu parceiro, ele pode anunciar que deu um passe geral e com isso ganhar de bnus 50 pontos. 3. Quando um jogador descarta sua ltima pea em uma rodada diz-se que ele "bateu, e, portanto ganhou a rodada. Com isso ele ganha de bnus 10 pontos e mais o mltiplo de 5 ligeiramente inferior soma dos valores constantes nas peas que sobraram nas mos dos adversrios (garagem). Se a batida for feita com uma carroa, o bnus de 20 pontos. 4. Quando ocorre uma situao onde nenhum dos jogadores consegue jogar, embora estejam com peas na mo, diz-se que o jogo est fechado. Neste caso ganha a rodada a dupla cuja soma dos valores das peas for o menor. A soma das peas da dupla adversria computada em seu favor, como no caso 3. Posio dos Jogadores: Os membros de cada dupla so colocados em posies alternadas, de forma que as jogadas (colocao de peas) seja feita de forma alternada entre as duplas adversrias. Por exemplo, em um jogo presencial, podemos usar, como suporte para colocao das peas, uma mesa de quatro lugares, ficando os parceiros sentados frente-a-frente. Distribuio das Peas: a distribuio das 28 peas entre os 4 jogadores deve ser feita de forma aleatria. Na prtica, em um jogo com peas fsicas, viram-se as peas de cara para baixo e mistura as peas com as mos. Cada jogador vai retirando as suas prprias peas. Quem comea uma rodada: Uma rodada sempre iniciada com a colocao de uma "carroa. Na primeira rodada do jogo, a carroa a ser utilizada de sena (6), cabendo pois ao jogador que a tirou comear o jogo. As rodadas seguintes so iniciadas pelo jogador que bateu a rodada anterior, com a carroa que ele preferir. Se ele no possui carroa, ele passa e o jogador seguinte (da dupla adversria) inicia o jogo, se este tambm no possuir, passa a frente. Uma Jogada: Estando na sua vez de jogar, o jogador deve escolher uma das pedras de sua mo, e coloca-la na mesa de jogo, combinando com alguma das pontas abertas. A escolha da pea a ser jogada deve contribuir para o objetivo da dupla que ganhar o jogo, isso significa, em linhas gerais, escolher uma pedra que me permita fazer o maior nmero de pontos e, quando isso no for possvel, escolher uma pedra que reduza o nmero de pontos que os adversrios possam fazer com base em minha jogada. H, entretanto algumas nuances a serem consideradas: Quando eu jogo fazendo pontos devo buscar maximizar meus pontos e minimizar os que o jogador adversrio possa fazer a partir da minha jogada; Se o jogo estiver prximo do trmino, e a dupla adversria ameaa completar os 200 pontos, pode ser desejvel adiar o trmino, no fazendo os pontos. Por exemplo, suponha que a dupla adversria tem 185 pontos e a minha 130. Se eu tiver uma pea na mo que faz 25 pontos, mas se possvel ao adversrio possuir 154 uma pea que lhe permita fazer 15 pontos, eu posso escolher outra pea, deixando assim de obter os 25 pontos; Quem abre uma rodada, bater a rodada a menos que passe. Tendo em vista que ao ganhar uma rodada, h bnus para a dupla, posso deixar de marcar ponto visando vencer a rodada; dem para tentar evitar que a dupla adversria ganhe a rodada (quando foro a passada de um adversrio que comeou a rodada, a batida passa para o meu parceiro). Um passe geral d um bnus de 50 pontos, isso pode me levar a busc-los, desde que as condies do jogo, definidas pelas peas de minha mo, combinadas com o que j foi jogado, se mostrem propcias. Exerccios: A seguir apresentamos alguns exerccios, baseados no domin. Para fins didticos separamos em grupos. Grupo I 1. Escreva a funo pedrap que associe um par a True se e somente se (sss) o par uma representao vlida para uma "pedra" e FaIse caso contrrio. ExempIos de uso: 1. pedrap (2, 7) ==> FaIse 2. pedrap ((-3), 4) ==> FaIse 3. pedrap (3,4) ==> True SoIuo: pedrap (x,y) = validap x && validap y validap x = elem x [0..6] 2. Escreva a funo maop que associe uma lista de pares de inteiros a True sss a lista uma representao vlida para a "mo" de um jogador e FaIse caso contrrio. ExempIos de uso: 1. maop [ ] True 2. maop [((-3), 4)] FaIse 3. maop [(3,4)] True 4. maop [ (1,2), (1,5), (2,0), (2,4), (3,3), (1,1), (0,0), (4,0)] FaIse SoIuo: maop [ ] = True maop (x:xs) = (length xs <= 6) && pedrap x && maop xs 155 3. Escreva a funo carrocap que associe um par a True sss o par uma "carroa" e FaIse caso contrrio. 4. Escreva a funo tem_carroca_p que associe uma "mo" a True sss a mo possuir pelo menos uma carroa e FaIse caso contrrio. 5. Escreva a funo tem_carrocas que associe a uma "mo" a lista das "carroas" nela contida. Grupo II Em vrios momentos do jogo faz-se necessrio saber a quantidade de pontos associado uma coleo de pedras. Em particular, no final do jogo, quem "sentou" a sua ltima pedra faz jus "garagem" que determinada a partir dos pontos que restaram na(s) mo(s) dos adversrios. 6. Escreva a funo pontos que associe uma lista de "pedras" a soma dos pontos das pedras nela contidos. Onde os pontos de uma pedra a soma de suas pontas. SoIuo: pontos [ ] = 0 pontos (x:xs) = ponto x + pontos xs where ponto (x,y) = x + y 7. Escreva a funo ,ara,e! que associe uma lista de "pedras" ao maior mltiplo de 5 (cinco), menor ou igual soma dos pontos nela contidos. 8. Escreva a funo pedraVi,ualVp que associe dois pares de inteiros a True sss representam a mesma pedra e FaIse caso contrrio. bom lembrar que a ordem das pontas irrelevante, assim (2,4) e (4,2) representam a mesma pedra. 9. Escreva a funo ocorreVpedraVp que associe uma "pedra" e uma "mo" a True sss a "pedra" ocorre na "mo" e FaIse caso contrrio. 10. Escreva a funo ocorreV$alorVp que associe um valor vlido para "ponta" e uma "mo" e produza True sss o valor ocorre em alguma pedra da mo e FaIse caso contrrio. 11. Escreva a funo ocorreVpedra que associe a um valor e uma "mo", uma lista contendo as pedras da "mo" que possuem o valor dado. 12. Escreva a funo pedraV!aior que associe uma "mo" a pedra de maior valor na "mo" dada. Uma pedra p1 maior que uma outra p2 sss a soma das pontas de p1 for maior que a soma das pontas de p2. 13. Escreva a funo ocorreV$alorVq que associe um valor e uma "mo" e produza o nmero de pedras na mo que possuem o valor dado. 14. Escreva a funo ocorreVcarrocaVq queassocie uma mo quantidade de carroas nela existentes. 15. Escreva a funo tiraV!aior que associe uma mo a uma lista similar "mo" de onde foi extrada a pedra de maior ponto. 156 16. Escreva a funo tiraV!aiorV$ que associe um valor e uma "mo" lista similar "mo" de onde se extraiu a pedra de maior pontos de um determinado valor para ponta. Grupo III O jogo se desenvolve pela colocao, pelo Jogador da vez, de uma pedra que combine com alguma das "pontas" da "mesa". Num momento genrico do jogo temos quatro pontas disponveis para execuo de uma jogada. Uma ponta pode ser simples ou uma carroa. As carroas so dispostas de tal forma que todos os seus pontos estejam para "fora". Chamaremos "mesa" lista de pontas disponveis para jogada. Pontas simples sero representadas por listas de um elemento e carroas por uma lista com dois elementos idnticos. Por exemplo, a "mesa" ilustrada na Figura 18.2 representada pela qudrupla ( [5,5], [5], [0],[4] ). Uma ponta ainda no aberta representada por lista vazia. Dizemos que h marcao de pontos em uma mesa quando a soma das pontas um mltiplo de 5. Os pontos a serem marcados a soma das pontas, com as carroas contando em dobro. Figura 18.2 desenvolvimento do jogo, no instante em que temos nas pontas externas, uma carroa de quina, uma quina, branco e quadra. 157 17. Escreva a funo !esap que associe uma qudrupla de listas a True sss a qudrupla for uma descrio vlida de "mesa". SoIuo: mesap (p1,p2,p3,p4) = vponta p1 && vponta p2 && vponta p3 && vponta p4 where vponta (x:y:xs) = if not (null xs) then FaIse eIse validap x && vponta (y:xs) vponta (x :[ ] ) = validap x validap x = elem x [0..6] 18. Escreva a funo carrocaV!Vp que associe uma mesa a True sss pelo menos uma das pontas for carroa. 19. Escreva a funo pontosV!arcados que associe uma mesa ao o nmero de pontos a serem marcados se a soma das pontas for mltiplo de cinco e zero em caso contrrio. 20. Escreva a funo podeVjo,asVp que associe uma "pedra" e uma "mesa" a True sss a pedra possui uma ponta que combina com pelo menos uma das pontas da mesa. 21. Escreva a funo !arcaVpontoVp que tenha como entrada uma "pedra" e uma "mesa" e produza True sss a pedra pode ser jogada fazendo pontos em uma das pontas da mesa. Lembre-se que as carroas devem ser contadas pelas duas pontas da pedra. 22. Escreva a funo !aiorVponto que tenha associa uma pedra e uma mesa ao nmero da "ponta" da mesa onde pode ser marcado o maior valor de ponto que ser marcado pela pedra. Considere que a em uma "mesa" as pontas so numeradas a partir de zero, da esquerda para a direita. 23. Escreva a funo jo,aVpedra que associe uma "pedra", uma "mesa" e um nmero de "ponta" da mesa a uma nova mesa obtida ao se jogar a "pedra" na "ponta" indicada. 24. Escreva a funo jo,ap que associe uma "mo" e uma "mesa" e produza True sss existe pelo menos uma pedra na mo que possa ser jogada em pelo menos uma ponta da mesa. Caso contrrio produza FaIse. 25. Escreva a funo jogada que associe uma "mo" e uma mesa ao nmero da pedra na mo e nmero da ponta na mesa onde pode ser feita a jogada que marque mais ponto. Considere inclusive jogada onde no h marcao de ponto. 26. Escreva a funo faz_jogada que associe uma "mo" e uma "mesa" e produza uma nova "mesa" obtida por se jogar marcando o maior nmero de pontos possvel 18.2 Banco de Sangue: para facilitar o atendimento da demanda por transfuses de sangue o sistema de sade criou os chamados Bancos de Sangue. Como sabemos, cada transfuso s pode ser realizada usando tipos de sangue apropriados. A adequao de um determinado tipo de sangue baseada em estudos cientficos que identificou quatro tipos 158 sangneos, denominados de 2, W, 2W e M. Outros estudos identificaram ainda a existncia do chamado fator RH que pode ser positivo (+) ou negativo (-), assim o sangue de qualquer indivduo classificado de acordo com esses dois atributos. Por exemplo, dizemos que fulano possui sangue tipo O e fator RH positivo, e abreviamos para O+. Em um dado Banco de Sangue, diariamente, so feitas doaes por pessoas de diferentes tipos sangneos, para as quais feito um registro contendo o nmero da carteira de identidade do doador (RG), o sexo (S), a data da doao (DD), a data de nascimento (DN), o tipo sangneo (TS), o fator RH (RH) e a quantidade doada (QD) (250 ou 500 ml). O sangue doado guardado em recipientes com uma capacidade fixa (250 ml). Tambm, diariamente so feitas requisies pelos hospitais (H), cada requisio indica as caractersticas do sangue (tipo e fator RH) e a quantidade solicitada (QS). Sabemos que homens e mulheres possuem intervalos de tempo diferentes para fazer doaes. Para homens o intervalo mnimo de 2 (dois) meses e para mulheres de 3 (trs). A idade mxima para doadores 60 anos. Sejam as seguintes estruturas Doao (RG, S, DD, DN, TS, RH, QD) Requisio (H, TS, RH, QS) Exerccios: Escreva programas em HUGS para resolver os seguintes problemas: 1. Dada uma lista de doaes, obtenha a quantidade total de sangue doado por tipo sangneo e fator RH. O resultado ser uma tupla (um item para cada combinao de tipo sangneo com fator RH) com triplas explicitando o tipo sangneo, o fator RH e a quantidade total. Quando no houver doao de uma dado par tipo-fator deve ser indicado o valor zero. Por exemplo: ( ('A', '+', 0), ... ('O','+', 5500) ...) 2. Para uma dada lista de doaes, determine a lista dos dias de um dado ms onde as doaes foram menores que a mdia mensal. 3. Dada uma lista de doaes e a data atual, determine a lista de doadores que j esto aptos a fazerem novas doaes. 4. Dada a lista de doadores e o ms, determinar o nmero de doadores que esto aptos a doar sangue naquele ms. (Essa e a questo 3 parecem anlogas, no?) 5. Determine a relao de doadores que fazem doaes com o maior ndice de regularidade. O ndice de regularidade dado pela nmero de vezes que o intervalo entre as doaes coincidem, dividido pelo nmero de doaes menos um. 6. Dada a lista de doadores, verificar o tipo sangneo que mais comumente doado. 7. Dada a lista de doadores e o ano, determine o ms em que houve mais doaes naquele ano. 159 8. Dada a lista de requisies de um determinado hospital, determinar a lista de tipos sangneos com os respectivos fatores RH, que possuem seus pedidos atendidos pelo banco de sangue. 9. Determinar, para um dado hospital em um determinado ano, a demanda mensal de sangue, por tipo sangneo e fator RH. 10. Determinar a lista de doadores que no esto mais aptos a fazer doaes, considerando a data atual. 11. Considere o estoque atual do banco de sangue, determinado pela funo estoque (prob 1), e uma lista com vrias requisies. Leve em conta que o estoque pode ser insuficiente para atender completamente todos os pedidos. Determine o estoque atualizado aps o atendimento dos pedidos e produza uma lista das requisies atendidas, constando a quantidade que foi de fato fornecida. 12. No problema 11, considere que voc deseja atender cada hospital solicitante, de forma proporcional ao seu pedido, considerando os pedidos de cada tipo sangneo separadamente. Por exemplo, suponha que: o total de pedidos de sangue O+ de 12.000 ml, que o hospital "h1 solicitou 3.000 ml de sangue O+ e que no estoque 8.000 ml. Podemos observar que o pedido para o sangue O+ do hospital "h1 representa 25 % do total. Neste caso o hospital "h1 ser atendido com 25 % de 8.000 ml que representa 2.000 ml. Produza uma lista como os pedidos atendidos e outra com os pedidos pendentes. 13. Considere a poltica de atendimento do problema 12 mas leve em conta que um dado pedido deve ser atendido completamente. Considere o exemplo do problema anterior, e suponha que os pedidos do hospital 'h1' para o sangue O+ so 4, ("h1, O, +, 1.500), ("h1, O, +, 1.000) e ("h1, O, +, 250) e ("h1, O, +, 500). Neste caso, considerando que os pedidos esto em ordem de prioridade, seriam atendidos os pedidos ("h1, O, +, 1.500) e ("h1, O, +, 250). 14. Modifique a poltica de atendimento do problema 14 para que o atendimento seja tal que o hospital "h1 use da melhor forma possvel a proporcionalidade que lhe cabe. No caso do exemplo apresentado no problema 14, o pedido de 250 ml seria descartado visto que atendendo o pedido de 500 ml o hospital 'h1 estar usando melhor a parte que lhe cabe. (escolher a combinao mais apropriada) 18.3 Correio EIetrnico: Considere um sistema de mensagens eletrnicas. Uma mensagem pode ser descrita por uma tupla contendo o remetente, o destinatrio, a data de envio, o assunto e o corpo da mensagem. Mensagens podem ser acumuladas em listas para posterior acesso. Para facilitar o acesso podemos construir ndices baseados nos dados contidos na mensagem. Por exemplo, podemos ter um ndice baseado no remetente para facilitar o acesso a todas as mensagens de um dado remetente. Considere as seguintes estruturas: mensagem (remetente, destinatrio, data, assunto, corpo) ndice [ (argumento1,[ordem na lista de mensagens]), (argumento2, []), ...] 160 Exerccios: Elabore programas em HUGS, usando recurso, para atender aos seguintes problemas. A interface (nome e parmetros) das funes dado em cada uma das questes. 1. (indexa msgs) Dada uma lista de mensagens, produza o ndice das mensagens por remetente. Um ndice ter a seguinte estrutura: [ (remetente1, lista de ocorrncias), (remetente2, lista de ocorrncias), ...] onde lista de ocorrncias formada pela posio na lista de mensagens onde o remetente ocorre. Por exemplo: [ ("jose@inf.ufes.br, [1, 23]), ("maria@inf.ufes.br, [10, 20, 50]), ...] 2. (consulta r p) Considere definidos um ndice por remetente, um ndice por palavras constantes no assunto das mensagens e uma lista de mensagens. Dados um remetente (r) e uma palavra(p), obtenha a lista de mensagens enviadas por r onde a palavra p ocorre no assunto. remetentes = [("remetente1, [...]),("remetente2, [...]), ... ] palav_assunto = [ ("palavra1. [...]),("palavra2. [...]), ... ] mensagens = [ mensagem11 mensagem21 mensagemL1 ] 3. (msgPmes a r msgs) Dado um ano (a), um remetente (r) e uma lista de mensagens (msgs), verificar a quantidade mensagens enviadas por r em cada ms. 4. (busca p ind msgs) Considere um ndice construdo na forma de lista (indb). O primeiro elemento um par com uma palavra (p) e a lista de mensagens (msgs) onde p ocorre, o segundo elemento uma lista no mesmo formato de indb, para as palavras menores que p e o terceiro para as palavras maiores que p ndice = [ (palavra1, [...]), ndice para palavras menores que p, ndice para palavras maiores que p] Quando no houver palavras menores ou maiores que uma dada palavra, o ndice igual a uma lista vazia. Fada uma pala7ra p1 o =ndice (indb) e a lista de mensagens (msgs)1 descre7a a lista mensagens onde p ocorre1 usando o =ndice dado 5. (palavPassunto msgs) Considere definida uma lista de palavras irrelevantes (lis). Dada uma lista de mensagens (msgs) produza um ndice com as palavras distintas que ocorrem nos assuntos das mensagens e no ocorrem em lis. Para cada palavra deve ser produzido tambm a lista das posies na lista de mensagens (msgs) onde ela ocorre. lis = [ "palavra1, "palavra2. ... ] 6. (releva msgs li lf) Dada uma lista de mensagens podemos obter uma lista de palavras relevantes. Define-se como palavra relevante em uma lista mensagens (msgs) aquelas 161 cuja freqncia satisfazem um intervalo para o qual so dados um limite superior (ls) e um limite inferior (li). 7. (constante msgs a) Dada uma lista de mensagens (msgs), determinar a lista de remetentes que enviaram pelo menos uma mensagem para cada ms de um dado ano a. 8. (freqData msgs m) Para uma dada lista de mensagens desejamos obter a quantidade de mensagens para cada dia de um dado ms m. 9. (identico indb1 indb2) Dados dois ndices no formato de rvore binria de pesquisa desejamos verificar se so idnticos. Dizemos que dois ndices so idnticos quando a palavras e as listas de ocorrncia coincidem e os subndices das palavras menores e o das palavras maiores respectivamente so idnticos. ndice = ( (palavra1, [...]), ndice para palavras menores que p, ndice para palavras maiores que p) 10. (palavOrd indb) Dado um ndice no formato de rvore binria de pesquisa produza uma lista das palavras nele contidas de tal forma que as palavras se apresentem em ordem alfabtica crescente. 11. (resgate indb) Dado um ndice no formato de rvore binria de pesquisa produza uma lista das palavras que ocorrem em cada mensagem. A lista resultante ter o seguinte formato: [ [palavras da mensagem de ordem 0], [palavras da mensagem de ordem 1 ], ... ] 12. (balance arbinpq) Uma rvore binria de pesquisa est balanceada se e somente se a quantidade (q1) de elementos no subindice das palavras menores difere da quantidade (q2) de elementos no subindice das palavras maiores de no mximo um (1) e q1 maior ou igual a q2. q2 + 1 %= q1 %= q2 13. (insOrd indb msg) Dados um ndice (indb) no formato de rvore binria de pesquisa e uma mensagem (msg), descreva a nova rvore obtida pela insero das palavras das mensagem (msg), exceto as irrelevantes. 14. (perfil msg diret fga) Um diretrio uma lista de assuntos, cada um dos quais associado a uma coleo de palavras. Dada uma mensagem e um diretrio podemos atribuir mensagem um perfil que uma lista de valores indicando o grau de aproximao (ga) dela com cada assunto. O ga de uma mensagem com respeito a um assunto pode ser obtido com base na freqncia com que as palavras a ele associadas ocorrem na mensagem. Considere que a funo que calcula o ga fornecida e opera sobre uma lista de inteiros. Considere os seguintes formatos: Diretrio M (Nassunto1O1 Mpala7ra11 pala7ra21 P)1 (Nassunto2O1 Mpala7ra11 pala7ra21 P)1 P Perfil M (Nassunto1O1 ga1)1 (Nassunto2O1ga2)1 P 162
18.4 Lista de Compras: Para realizar um determinado projeto necessitamos adquirir certos componentes eletrnicos. No mercado de componentes existem vrios fornecedores que vendem seus produtos com preos diferenciados. A escolha da melhor alternativa para satisfao de nossas compras depende de vrios fatores, dos quais o melhor preo um dos mais importantes. Considere as seguintes definies: Tabela de Preo Uma lista contendo todos os preos dos componentes comercializados por um determinado revendedor. Cada elemento da lista um par no formato (material, preo). Exemplo: [("potenciometro, 2.50), ("resistor-100k, 0.30), ("capacitor-10mF,0.50), ("indutor- 10R,3.00)] Pedido de Compra Uma lista contendo todos os materiais necessrios para um determinado projeto, com suas respectivas quantidades. Cada elemento da lista um par no formato (material, quantidade). Exemplo: [("transformador, 50), ("fonte DC, 10), ("resistor-200k,100)] Lista de Revendedores Uma lista contendo a tabela de preos de todos os revendedores, no formato: [ (revendedor1, [tabela de preo] ), ...]
Exerccios: Elabore programas em HUGS, usando recurso, para atender aos seguintes problemas. O nome das funes dado em cada uma das questes. 1. (custo) Dado o resultado da pesquisa de preos de um pedido de compra, para um certo fornecedor, no formato [ (material1, qtde, preo), (material2, qtde, preo), ... ], queremos obter o custo total da compra se optarmos por esse fornecedor. 2. (fornece) Dado um pedido de compra e a tabela de preos de um determinado revendedor, obtenha a lista dos materiais que ele pode fornecer. 3. (subprojeto) Considere os pedidos de compra para dois projetos (p1 e p2). Eventualmente alguns itens do pedido do projeto p1 podem ocorrer no pedido de p2, possivelmente com quantidades distintas. Dizemos que um projeto p1 subprojeto de p2 se cada componente de p1 tambm componente de p2 em quantidade idntica ou inferior. 4. (lfornecedor) Dado um pedido de compra e a lista de revendedores, descrever a lista de fornecedores para cada componente, com seus respectivos preos, no formato [(material1,[rev1, rev2,...] ), (material2, [...]), ...] 18.5 Gerncia Acadmica: Considere a gerncia acadmica dos cursos de graduao de uma universidade. As disciplinas cursadas por um aluno so registradas em seu histrico. O registro deve conter o cdigo da disciplina, o ano e o semestre em que foi cursada e a nota obtida. Semestralmente o aluno deve requerer matrcula em novas disciplinas. O pedido de matrcula realizado atravs da apresentao das disciplinas desejadas pelo aluno. Um dos critrios para conseguir se matricular em uma disciplina que o aluno tenha cumprido, com aprovao, os pr-requisitos da disciplina. Um aluno aprovado em uma disciplina se obtiver mdia superior ou igual a 5 (cinco). Cada curso possui uma grade curricular que relaciona cada disciplina do curso com seus respectivos pr-requisitos. 163 Considere as seguintes definies: Histrico Escolar Um par contendo o cdigo de matrcula do aluno e a lista de disciplinas cursadas. !ada disciplina cursada representada por uma tripla com o cdigo da disciplina, o ano e o semestre que ela foi cursada e a nota obtida. "ormato# $matrcula, % $disciplina, $ano, semestre&, nota&, ... '& onde matrcula ( $ano, curso, n)mero*dentifica+o*ndi,idual& !adastro de -isciplinas Uma lista contendo, para cada disciplina, um par com o cdigo da disciplina e a quantidade de crditos. Grade !urricular Um par com o cdigo do curso e uma lista de pares onde cada par representa uma disciplina $cdigo& e uma lista dos seus pr. requisitos. "ormato# $curso, %$disciplina, %disciplina, disciplina,...'&, ...'& /edido de matrculaUm par com o cdigo do aluno e uma lista contendo o cdigo das disciplinas nas quais o aluno dese0a se matricular. "ormato# $matrcula, %pedido1, pedido2, ...'& Oferta Uma lista contendo as turmas a serem ofertadas, com seus 3or4rios e limite de ,agas. "ormato# %$disciplina, turma, quantidade5de5,agas, 3or4rio&,...' onde 3or4rio ( %$dia5da semana, 3ora5inicial, 3ora5final&, ...' 6ista de disciplinas de um curso, apresentadas por perodo Um par contendo o cdigo do curso e uma lista de pares, onde cada par representa um perodo do curso e uma lista das disciplinas do perodo. "ormato# $curso, %$perodo, %disciplina, disciplina,...'& Histrico Escolar Um par contendo o cdigo de matrcula do aluno e a lista de disciplinas cursadas. Cada disciplina cursada representada por uma tripla com o cdigo da disciplina, o ano e o semestre que ela foi cursada e a nota obtida. Formato: (matrcula, [ (disciplina, (ano, semestre), nota), ... ]) onde matrcula = (ano, curso, registro) Cadastro de Disciplinas Uma lista contendo, para cada disciplina, um par com o cdigo da disciplina e a quantidade de crditos. Grade Curricular Um par com o cdigo do curso e uma lista de pares onde cada par representa uma disciplina (cdigo) e uma lista dos seus pr- requisitos. Formato: (curso, [(disciplina, [disciplina, disciplina,...]), ...]) Lista de disciplinas de um curso, apresentadas por perodo Um par contendo o cdigo do curso e uma lista de pares, onde cada par representa um perodo do curso e uma lista das disciplinas do perodo. Formato: (curso, [(perodo, [disciplina, disciplina,...]) Exerccios: Escreva funes em Hugs para resolver os problemas propostos abaixo, usando os nomes indicados em negrito. 7 pala,ra semestre, nestes problemas, representa um elemento do tipo $ano,s&, onde s ( 1 ou s ( 2. 164 1. (credse) Dado um histrico, o cadastro de disciplinas e um semestre, descrever o total de crditos cumpridos no semestre. 2. (ncred) Dado um histrico e o cadastro de disciplinas, descrever a quantidade de crditos cumpridos por semestre. 3. (dncursadas) Dado um histrico e uma lista com as disciplinas por perodo, descrever a lista das disciplinas no cursadas ainda pelo aluno, dos perodos j cumpridos. Esta lista deve ter o seguinte formato: [(perodo,[disciplina,..]),(perodo,[disciplina,...]),...] 4. (maxcred) Dada uma lista de histricos, identificar a lista de alunos que possuem o maior nmero de crditos cumpridos. 5. (conterep) Dado um histrico, contar o nmero de reprovaes. 6. (jubiIa) Verificar se um determinado aluno se encontra em situao de jubilamento (possui trs reprovaes em uma mesma disciplina). 7. (abandono) Verificar se o aluno deixou de cursar disciplinas em algum semestre. 8. (divida) Obter a lista das disciplinas que foram cursadas por um dado aluno, nas quais ele ainda no obteve aprovao. 9. (reprova) Obter uma lista de reprovaes por perodo, para um dado aluno. 10. (crend) O coeficiente de rendimento (CR) de um dado aluno determinado pela mdia ponderada das notas obtidas, tendo como peso, o nmero de crditos das disciplinas. Determine o CR de um dado aluno. 11. (semprereq) Obter uma lista das disciplinas de um dado curso, que no possuem pr-requisito. 12. (cumprereq) Dada uma disciplina, verificar se o aluno cumpriu seus pr-requisitos. 13. (dividas) Obter para um dado aluno, a lista de disciplinas que ele ainda no cursou, com respeito a uma dada grade curricular. 14. (sugestao) Obter para um dado aluno, a lista de disciplinas que ele est apto a cursar, com respeito a uma dada grade curricular. 15. (Icumprereq) Obter uma lista de pedidos de disciplinas para as quais os pr- requisitos foram cumpridos. 16. (IdiscsoI) Dada uma lista com os pedidos de todos os alunos, obtenha uma lista das disciplinas solicitadas, sem repetio. 17. (soI_discipIinas_ord) Obter a lista de alunos que solicitaram uma dada disciplina, com seus respectivos coeficientes de rendimentos. A lista de alunos deve estar em ordem crescente por coeficiente de rendimento. A lista resultante deve ter o seguinte formato: [(aluno1, coeficiente_de_rendimento1),(aluno2, coeficiente_de_rendimento2), ...] 18.6 Agncia de Turismo: Uma agncia de turismo possui o mapa atualizado de reservas dos hotis de uma determinada regio. Para cada hotel, todas as informaes pertinentes aos tipos de quarto oferecidos so registrados no mapa. Este mapa representado por uma lista de tuplas, seguindo o seguinte formato: 165 [(nome_hoteI, [(tipo_quarto, vaIor_diria, situao), ...] ), ...] onde nome_hoteI do tipo string; tipo_quarto uma string; vaIor_diria um nmero real positivo; situao: nmero inteiro positivo que indica a quantidade de quartos livres do tipo indicado. Para o melhor entendimento da representao da lista de hotis no hugs, considere o seguinte exemplo: lhoteis = [("peterle", [("simples", 50.0, 5),("duplo", 75.8, 10), ("luxo", 110.0, 2)] ), ("ibis", [("simples", 45.0, 3),("duplo", 65.5, 15), ("luxo", 95.0, 3)] ), ("novotel", [("simples", 65.6, 10),("duplo", 90.5, 20), ("luxo", 150.0, 10)] )] Exerccios: Considerando o formato desta lista de hotis apresentada acima, resolva o seguinte problema, descrevendo funes em HUGS. 1. Dados um tipo de quarto e a lista de hotis, apresente a lista com o nome do hotel ou com os nomes dos hotis que possuem a oferta mais barata para aquele tipo de quarto. 2. Avalie a funo definida no problema 1 para os parmetros adequados, considerando a lista de hotis como a dada acima. 18.7 EspetcuIos Teatrais: Vrios espetculos esto sendo apresentados em um grande teatro da cidade. Para cada um dos espetculos, registra-se o mapa de ocupao da platia, conforme as vendas dos ingressos. A platia est representada por m filas numeradas de 1 a m, sendo que cada fila contm n cadeiras tambm numeradas de 1 a n Considere a seguinte representao para os dados: Lugar na platia ('ila, cadeira)# onde 'ila 2 representada por um inteiro de 1 a m e cadeira# por um inteiro de 1 a n. Platia 3ista de duplas (lu(ar, situao) sendo que a situa&o 2 : 1 para indi"ar lugar o"upado e 0 para indi"ar lugar vago. Teatro 3ista de duplas (es"etbculo, "lat!ia) onde es"etbculo 2 representado por um inteiro de 1 a p. Exerccios: Escreva um script em HUGS, com funes que resolvam os problemas abaixo. Nomes para cada uma das funes so sugeridos ao final do enunciado de cada problema. 5. Dada uma platia pls, descreva a quantidade total de lugares ocupados (totalOcup). 6. Dado um lugar lg e uma platia pls, verifique se o lugar lg est livre (estaLivre). 7. Dado um lugar lg e uma platia pls, verifique se existe algum vizinho lateral de lg que est livre (vizinhoLivre). 166 8. Dada uma fila fl e uma platia pls, descreva a lista de cadeiras livres da fila fl (cadeirasLivresFila). 9. Dada uma platia pls, descreva a lista de cadeiras livres para cada fila (lugLivresFila) 10. Dada uma platia pls, descreva a(s) lista(s) com o maior nmero de cadeiras livres (filaMaxLivre). 11. Dado um teatro trs e um espetculo ep, descreva a sua platia (plateiaEsp). 12. Dado um teatro trs, um espetculo ep e uma fila fl, descreva a lista de cadeiras livres da fila fl (cadeirasLivresFilaEsp). 167 19. ENTRADA E SAIDA DE DADOS 19.1 INTRODUO: Os programas que apresentamos e propusemos at ento, se basearam em um padro especfico de interao entre o usurio e o sistema. O interpretador HUGS est constantemente solicitando uma expresso, que em seguida avaliada por ele e o resultado apresentado ao usurio. Vejamos o modelo ilustrado na figura 20.1. A avalio de uma expresso leva em conta as definies disponveis em bibliotecas e as construdas pelos programadores. Algumas vezes entretanto este modelo de interao no adequado tendo em vista que o programa do usurio no pode tomar a deciso de pedir ou no um dado ao usurio. Tudo tem que ser informado antecipadamente. ExempIo 1: Desejamos fazer um programa que interaja com o usurio para que ele tente descobrir um nmero oculto. A interao do programa s deve continuar enquanto o jogador no descobrir o nmero oculto. E veja, no d para antecipar antes e codificar isto em uma expresso. A Figura 20.2 mostra um esquema da interao necessria. O programa pode comear pedindo um valor ao usurio. Se o usurio fornece um valor idntico ao valor oculto, o programa exibe a mensagem anunciando que o jogador venceu e encerra o jogo. Caso contrrio o programa solicita um novo palpite. 168 usurio Interpretador HUGS expresso resuItado Figura 20.1 Modelo de nterao usurio-interpretador HUGS scripts Nestes casos, no queremos descrever mapeamentos entre conjunto de valores e sim seqncia de aes. o contato das funes com o mundo. Ainda podemos usar funes para determinar valores, mas quando precisamos, temos que estabelecer uma seqncia de aes a serem executadas. Agora o que nos interessa definir programas, atravs de seqncias de aes. Vejamos uma soluo para o joguinho acima introduzido: jogo1 = do putStrLn ">>fornea um nmero entre 0 e 10" valor <- getLine if valor == "5" then do putStrLn "acertou - parabns!" else do putStrLn "errou - tente outra vez!" jogo1 sim no "Fornea outro vaIor" vaIor "Jogo encerrado" VaIor fornecido = Nmero ocuIto ? Figura 20.2 nteraes para descobrir um nmero oculto 169 E algumas interaes: Main> jogo1 >>fornea um nmero entre 0 e 10 3 errou - tente outra vez! >>fornea um nmero entre 0 e 10 4 errou - tente outra vez! >>fornea um nmero entre 0 e 10 2 errou - tente outra vez! >>fornea um nmero entre 0 e 10 5 acertou - parabns! Vamos agora dissecar o programa: 1. Para estabelecer contato com o mundo precisamos de uma ao para leitura e outra para exibio de resultado. Aqui a leitura realizada atravs da primitiva getLine. Para exibir resultados fazemos uso da primitiva putStrLn. Enquanto getLine no precisa de um parmetro, visto que uma ao que sempre buscar na interao obter um valor, a putStr necessita de um valor para ser exibido; 2. O programa jogo1 no precisa de parmetro para realizar sua atividade, dado que tudo ser solicitado diretamente dentro do corpo do programa, na medida do necessrio; 3. Para informar que o que estamos definindo uma seqncia de aes e no uma descrio funcional, introduzida a partcula do, que pode ser traduzida pelo imperativo faa! 4. Para que um valor obtido pelo comando de leitura getLine, se torne disponvel para uso do programa precisamos internaliz-lo atravs do uso da primitiva representada pelo smbolo "; valor getLine 5. Para indicar que desejamos continuar executando o programa podemos fazer uma chamada a ele mesmo, como o fizemos em: else %o putKtrAn "errou - tente outra vez!" >ogo1 170