• Nenhum resultado encontrado

2 Aplicação Pilha: Avaliador de Expressões Simples

N/A
N/A
Protected

Academic year: 2021

Share "2 Aplicação Pilha: Avaliador de Expressões Simples"

Copied!
5
0
0

Texto

(1)

Pr´atica de Algoritmos e Estrutura de Dados I – DIM0426 Umberto Souza da Costa - 11/09/2007

– Terceira Lista de Exerc´ıcios – Utiliza¸c˜ao dos TADs Pilha e Fila

1

Objetivos

O objetivo deste exerc´ıcio de programa¸c˜ao ´e utilizar as estruturas de dados do tipo pilha e fila para a resolu¸c˜ao de problemas pr´aticos. Ambas as classes j´a devem ter sido imple-mentadas como classes gen´ericas. A aplica¸c˜ao dos TAD pilha e fila ser´a na avalia¸c˜ao de express˜oes simples, cujo detalhes encontram-se descritos na Se¸c˜ao 2.

2

Aplica¸

ao Pilha: Avaliador de Express˜

oes Simples

A aplica¸c˜ao consiste em desenvolver um programa capaz de receber express˜oes simples e retornar o resultado de sua avalia¸c˜ao.

2.1 Express˜oes

O programa aves (Avaliador de Express˜oes Simples) dever´a lidar com express˜oes aritm´e-ticas simples envolvendo inteiros, formadas por:

⊲ constantes num´ericas inteiras, no intervalo de −32768 a 32767; ⊲ operadores (+, −, /, ∗,ˆ, %), com precedˆencia descrita em Tabela 1, e; ⊲ delimitadores de escopo (i.e. parˆenteses).

Precedˆencia Operadores Associa¸c˜ao

1 () −→

2 ^ ←−

3 * / % −→

4 + - −→

Tabela 1: Precedˆencia e ordem de associa¸c˜ao de operadores em express˜oes aves. O operador % representa o resto da divis˜ao inteira e o ˆ representa a opera¸c˜ao de exponencia¸c˜ao.

Segue abaixo exemplos de express˜oes v´alidas para o aves: ⊲ 35 - 3 * (-2 + 5)^2

⊲ 54 / 3 ^ (12%5) * 2 ⊲ ((2-3)*10 - (2^3*5))

O fim de linha (‘\n’) ser´a o indicador de fim de express˜ao, ou seja, o programa dever´a receber uma express˜ao por linha de entrada de dados.

(2)

2.2 A Tarefa

Sua tarefa consiste em elaborar um programa em C++ denominado aves.cpp que dever´a receber, via entrada padr˜ao (cin + getline)1, uma s´erie de express˜oes aves de at´e 256 caracteres, v´alidas ou inv´alidas.

O programa dever´a, ent˜ao, avaliar cada express˜ao e imprimir seu respectivo resultado na sa´ıda padr˜ao, cout, e em um arquivo texto de resultados (log.txt). Note que em caso de haver algum problema com a express˜ao (e.g. escopo aberto, operador inv´alido, falta de operando, etc.) o programa dever´a indicar tal erro claramente nas sa´ıdas definidas.

Por exemplo, a resposta que o programa deveria oferecer para as express˜oes exemplo da Se¸c˜ao 2.1 seria: 8, 12 e −50 (uma resposta por linha).

2.3 Solucionando o Problema

Uma das poss´ıveis solu¸c˜oes para o problema descrito nas se¸c˜oes anteriores ´e transformar a express˜ao original do formato infixo para o formato posfixo2. A transforma¸c˜ao de um formato para outro apresenta trˆes vantagens:

1. Durante o procedimento de transforma¸c˜ao ´e poss´ıvel detectar alguns erros de forma-¸c˜ao de express˜ao;

2. A express˜ao no formato posfixo n˜ao necessita da presen¸ca de delimitadores (parˆen-teses) por ser uma representa¸c˜ao n˜ao-amb´ıgua;

3. O algoritmo para avaliar uma express˜ao posfixa ´e mais simples do que um algoritmo para avaliar uma express˜ao infixa.

2.3.1 Nota¸c˜oes de Express˜oes

Uma express˜ao para somar A e B pode ser descrita como A + B. Essa representa¸c˜ao ´e denominada de forma infixa. Al´em dessa existem outras duas representa¸c˜oes, a saber:

+ A B prefixa A B + posfixa

Na nota¸c˜ao prefixa o operador (no caso o ‘+’) ´e introduzido antes de seus dois operandos (A e B). J´a na nota¸c˜ao posfixa o operador aparece logo ap´os seus dois operandos.

As regras que devem ser consideradas durante o processo de convers˜ao s˜ao: i) as opera¸c˜oes com a precedˆencia mais alta s˜ao convertidas em primeiro lugar (quando existe ambig¨uidade) e; ii) uma express˜ao convertida para a forma posfixa deve ser tratada como um ´unico operando. Veja na Tabela 2 alguns exemplos adicionais de convers˜ao da forma infixa para a posfixa.

2.3.2 Avaliando Uma Express˜ao Posfixa

Para realizar a avalia¸c˜ao de uma express˜ao na forma posfixa pode-se utilizar uma estru-tura de dados do tipo pilha. Cada vez que um operando (i.e. uma constante inteira) ´e

1

Usar while ( cin.getline(...) ) de forma que para encerrar a entrada de dados basta pressionar <Ctrl+D>. Tamb´em ´e poss´ıvel redirecionar a entrada de dados a partir de um arquivo ascii com o comando < na linha de comando. Ex.: $ ./aves < expressoes.txt.

2

(3)

Forma Infixa Forma Posfixa

A + B AB+

A + B − C AB + C−

(A + B) ∗ (C − D) AB + CD − ∗

AˆB ∗ C − D + E/F/(G + H) ABˆC ∗ D − EF/GH + /+ ((A + B) ∗ C − (D − E)) ˆ(F + G) AB + C ∗ DE − −F G+ˆ A − B/(C ∗ D ˆE) ABCDE ˆ∗/−

Tabela 2: Exemplos de equivalˆencias entre forma infixa e posfixa de express˜oes.

encontrado na express˜ao o mesmo deve ser introduzido na pilha. Quando um operador (i.e. +, ˆ, ∗, etc.) ´e encontrado na express˜ao, os dois elementos no topo da pilha s˜ao seus operandos. Portanto, devemos retirar esses dois elementos da pilha, realizar a opera¸c˜ao indicada pelo operador3e, a seguir, (re)introduzir o resultado de volta na pilha, tornando-o dispon´ıvel para uso como operando do pr´oximo operador. Confira o Algoritmo 1.

Algoritmo 1 Avalia¸c˜ao de express˜ao no formato posfixo.

01: fun¸c~ao AvalPosfixa( LPosfixa: Tipo Lista Componentes ): Inteiro 02: | % A Lista com express~ao na forma posfixa j´a foi gerada em LPosfixa

03: | Enquanto ( !LPosfixa.IsEmpty() ) Fa¸ca 04: | | symb = pr´oximo n´o de LPosfixa; 05: | | Se ( symb ´e um operando ) Ent~ao 06: | | | P.push( symb );

07: | | Sen~ao

08: | | | opnd2 = P.pop( ); 09: | | | opnd1 = P.pop( );

10: | | | valor = resultado de aplicar symb `a opnd1 e opnd2; 11: | | | P.push( valor ); % Resultado de volta `a pilha

12: | | Fim-Se 13: | Fim-Enquanto

14: | % Retornar o valor final da express~ao

15: x Retorne( P.pop( ) ). % S´o deveria haver um elemento

2.3.3 Convertendo Express˜oes: de infixa para posfixa

Para realizar a avalia¸c˜ao da express˜ao conforme descrito na Se¸c˜ao 2.3.2, faz-se necess´ario converter a express˜ao de infixa para posfixa. Essa convers˜ao deve ser feita de tal forma a lidar de forma correta com, digamos, os casos A + B ∗ C e (A + B) ∗ C — produzindo, respectivamente, as express˜oes ABC ∗ + e AB + C∗.

Ao analisar os casos acima percebe-se que o algoritmo de convers˜ao deve possuir algum tipo de mecanismo para armazenar os operadores temporariamente de tal forma que a regra de precedˆencia de operadores seja respeitada. Esse mecanismo de armazenamento tamb´em ser´a uma estrutura de dados do tipo pilha.

O Algoritmo 2 apresenta uma forma de converter uma express˜ao (sem parˆenteses4) no formato infixo para o posfixo. Para tanto assuma que existe uma fun¸c˜ao denominada

3

Cuidado com a ordem dos operandos, pois a pilha ´e uma TAD LIFO (Last In, First Out).

4

(4)

prcd(op1,op2)— onde op1 e op2 s˜ao operadores — que retorna VERDADEIRO se op1 tiver precedˆencia sobre op2 ou ambos tiverem a mesma precedˆencia, e FALSO caso contr´ a-rio. Por exemplo, prcd(‘*’,‘+’) e prcd(‘-’,‘+’) retornam VERDADEIRO, enquanto prcd(‘+’,‘*’)´e FALSO.

Algoritmo 2 Convers˜ao de express˜ao no formato infixo para posfixo.

01: fun¸c~ao Infx2Psfx( LInfixa: Tipo Lista Componentes ): Tipo Lista Componentes 02: | LPosfixa = lista vazia de componentes de express~ao;

03: | OP = uma pilha vazia de operadores ; 04: | Enquanto ( !LInfixa.IsEmpty() ) Fa¸ca 05: | | symb = pr´oximo n´o de LInfixa; 06: | | Se ( symb ´e um operando ) Ent~ao 07: | | | LPosfixa.Insert( symb ); 08: | | Sen~ao

09: | | | Enquanto ( !OP.IsEmpty() E prcd( OP.top(),symb ) ) Fa¸ca 10: | | | | topsymb = OP.pop( ); 11: | | | | LPosfixa.Insert( topsymb ); 12: | | | Fim-Enquanto 13: | | | OP.push( symb ); 14: | | Fim-Se 15: | Fim-Enquanto

16: | % Elimina¸c~ao de quaisquer operadores restantes

17: | Enquanto ( !OP.IsEmpty() ) Fa¸ca 18: | | topsymb = OP.pop();

19: | | LPosfixa.Insert( topsymb ); 20: | Fim-Enquanto

21: x Retorne LPosfixa.

O interessante do algoritmo s˜ao as linhas 09–12, que indicam o seguinte: se o operador da express˜ao for de menor precedˆencia do que aquele que est´a atualmente no topo da pilha ent˜ao desempilhe (linha 10) at´e achar um operador de menor precedˆencia (em outras palavras, um ‘+’ n˜ao pode ficar sobre um ‘*’ na pilha OP); simultaneamente envie os operadores desempilhados para a sa´ıda (linha 11).

Teste o algoritmo acima com as seguintes entradas: A ∗ B + C ∗ D e A + B ∗ CˆDˆE. Agora pense nas modifica¸c˜oes necess´arias que devem ser feitas no algoritmo e na fun¸c˜ao prcd de forma a acomodar o uso de parˆenteses (que pode modificar a precedˆencia dos operadores).

2.4 Implementa¸c˜ao

Inicialmente fa¸ca um levantamento de todos os erros poss´ıveis que podem acontecer na compila¸c˜ao de uma express˜ao. Documente cada erro, atribuindo-lhes um c´odigo ´unico, associando-os `a mensagens de erros significativas (i.e. objetivas e auto-explicativas) — essa abordagem facilita a parte de tratamento de erros de avalia¸c˜ao da express˜ao. De uma forma geral ´e poss´ıvel separar os erros em categorias: entrada de dados inv´alidas (e.g. n´umeros em ponto flutuante ou inteiros fora dos limites especificados), elabora¸c˜ao incorreta da express˜ao (e.g. falta de parˆenteses e s´ımbolos n˜ao reconhecido), e erro na avalia¸c˜ao da express˜ao (e.g. divis˜ao por zero, falta operando).

(5)

A

odigo Exemplo de Leitura de Dados

Nesta Se¸c˜ao temos um exemplo de como realizar a leitura de dados da entrada padr˜ao, o que permite realizar o redirecionamento para um arquivo de entrada.

Algoritmo 3 C´odigo exemplo de leitura de dados.

#include <iostream> #include <sstream> using namespace std; int main( void ) {

// expects either space-delimited numbers or lines that start with // two forward slashes (//)

string s;

// While there is input to read... while( getline( cin, s ) ) {

// In case the string starts with // we ignore it completely if( s.size() >= 2 && s[0] == ’/’ && s[1] == ’/’ ) {

cout << " ignoring comment: " << s << endl; }

else {

// Transforms s into a string stream. This is necessary // in order to extract the numbers inside the string. istringstream ss( s );

int d;

// Estract first number into d variable. while( ss >> d ) {

// Just output variable.

cout << " got a number: " << d << endl; }

} } }

Referências

Documentos relacionados

Para analisar as Componentes de Gestão foram utilizadas questões referentes à forma como o visitante considera as condições da ilha no momento da realização do

Todavia, nos substratos de ambos os solos sem adição de matéria orgânica (Figura 4 A e 5 A), constatou-se a presença do herbicida na maior profundidade da coluna

Belo Horizonte (SMED/BH) um espaço para dialogar sobre a educação de seus filhos. Em cada encontro é indicado um tema para ser debatido com as escolas. A frequência

Desta forma, a qualidade higiênico-sanitária das tábuas de manipulação dos Laboratórios de Técnica Dietética, Tecnologia de Alimentos e Gastronomia e das

Exerc´ıcio 14. Em uma corrida de F ´ormula 1, a equipe mostra ao piloto Rubens, que est´a em segundo lugar, 280 mil´esimos de segundo atr´as do primeiro, a partir da 38 a volta,

Para avaliar a eficiência da formulação utilizada para determinação das grandezas de interesse, foram analisados cinco exemplos. Uma vez que a integração analítica limita-se ao

modo favorável; porem uma contusão mais forte pode terminar-se não só pela resolução da infiltração ou do derramamento sanguíneo no meio dos. tecidos orgânicos, como até

In: VI SEMINÁRIO NACIONAL DE PESQUISADORES DA HISTÓRIA DAS COMUNIDADES TEUTO-BRASILEIRAS (6: 2002: Santa Cruz do Sul).. BARROSO, Véra Lúcia