Aula 14 Programação Baseada em Objectos Desenho de TAD Desenvolver uma calculadora Operações: + – adição - – subtracção / – divisão * – multiplicação ^ – potenciação 2 Suporte para double (incluindo negativos) Parênteses proibidos Expressões de cin Resultados em cout Expressões terminam em ; ou . Símbolo . termina execução da calculadora Introdução à Programação 2003/2004 Exemplo Entrada: 2 + 2 ; 2 * 2.5 . Saída: 4 5 3 Introdução à Programação 2003/2004 Exercício Qual o resultado de 2 * 3 + 3 * 5 ^ 2 ; ? E se os símbolos forem lidos um de cada vez? (Símbolos são valores ou operadores, incluindo terminadores) 4 Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: 5 1 Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: 6 + Que fazer aos símbolos lidos? Ao 1 e ao +? É necessário guardá-los algures! Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: 7 4 Podemos calcular já 1 + 4? Não! Depende do que se segue! Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: * Memorizar… No nosso programa teremos de guardar os símbolos algures! 8 Calcular já 1 + 4 teria sido asneira! A não ser que se tivesse lido 1 * 4, ou se o símbolo lido fosse + Nesse caso podíamos fazer já o cálculo, e escusávamos de memorizar tantos símbolos Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: 9 3 Introdução à Programação E a saga continua 2003/2004 Cálculo de expressão Símbolo lido: Podemos calcular o produto anterior, ou seja, 4 * 3! O resultado do produto tem de ser guardado O produto é o dos dois últimos valores lidos Regra: / Por ordem inversa à da leitura! Podem-se empilhar os operadores! 10 Introdução à Programação Ao ler um operador, podemos calcular os operadores em espera enquanto forem de precedência maior ou igual 2003/2004 Cálculo de expressão Símbolo lido: 2 Mais uma vez é necessário guardar o valor Se a próxima operação for, por exemplo, +, a divisão é calculada entre 12 e 2, que foram os últimos valores considerados Também se podem empilhar os valores! 11 Introdução à Programação 2003/2004 Regressemos ao início 12 Vamos recomeçar o cálculo, mas equipandonos com duas pilhas: Pilha dos operadores Pilha dos valores Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: operadores 13 1 Lido um valor, há que colocá-lo na respectiva pilha valores Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: + Lido um operador, há que verificar se há algum outro de maior ou igual precedência na pilha Como não há, há que o colocar na pilha apropriada 1 operadores 14 valores Introdução à Programação 2003/2004 Cálculo de expressão Símbolo lido: 4 + 1 operadores valores 15 Introdução à Programação Valores vão sempre para a pilha 2003/2004 Cálculo de expressão Símbolo lido: * 4 + 1 operadores valores 16 Introdução à Programação Como a multiplicação tem maior precedência que a soma, que está no topo da pilha, não se pode ainda realizar a soma É necessário, de qualquer forma, colocar a multiplicação no topo da pilha 2003/2004 Cálculo de expressão Símbolo lido: 3 * 4 + 1 operadores valores 17 Introdução à Programação É um valor… 2003/2004 Cálculo de expressão Símbolo lido: / 3 * 4 + 1 operadores valores 18 No topo da pilha há um operador com igual precedência Antes de guardar a nova operação na pilha, efectua-se a do topo e guarda-se o resultado = Introdução à Programação 12 2003/2004 Cálculo de expressão Símbolo lido: 2 / 12 + 1 operadores valores 19 Introdução à Programação Valores vão directos para a pilha, como sempre 2003/2004 Cálculo de expressão Símbolo lido: ^ 2 / 12 + 1 operadores valores 20 Introdução à Programação Como a potenciação tem maior precedência que a divisão, que está no topo da pilha, não se pode ainda realizar a divisão É necessário, de qualquer forma, colocar a potenciação no topo da pilha 2003/2004 Cálculo de expressão Símbolo lido: 3 ^ 2 / 12 + 1 operadores valores 21 Introdução à Programação Pilha com ele! 2003/2004 Cálculo de expressão Símbolo lido: - Têm de se realizar todas as operações com precedência maior ou igual! 3 ^ 2 / 12 + 1 operadores valores 22 = Introdução à Programação 8 2003/2004 Cálculo de expressão Símbolo lido: - Têm de se realizar todas as operações com precedência maior ou igual! 8 / 12 + 1 operadores valores 23 = Introdução à Programação 1,5 2003/2004 Cálculo de expressão Símbolo lido: - = 1,5 + 1 operadores valores 24 Têm de se realizar todas as operações com precedência maior ou igual! Depois, guarda-se a operação na pilha Introdução à Programação 2,5 2003/2004 Cálculo de expressão Símbolo lido: 2 - 2,5 operadores valores 25 Introdução à Programação Mais um… 2003/2004 Cálculo de expressão Símbolo lido: . = 2 - 2,5 operadores valores 26 Ao atingir o símbolo . (ponto), efectuam-se todas as operações que restam na pilha Introdução à Programação 0,5 2003/2004 Cálculo de expressão Símbolo lido: Ao atingir o símbolo . (ponto), efectuam-se todas as operações que restam na pilha O resultado retira-se da pilha dos valores: 0,5 operadores 27 valores Introdução à Programação 2003/2004 E agora? É necessário escrever o programa… Mas já se sabe que ferramentas estão em falta: 28 Um TAD para representar operadores Um TAD para representar uma calculadora E… Introdução à Programação 2003/2004 TAD Operador Representa operadores (binários) que podem ser +, -, *, / ou ^ Construtor deve receber símbolo indicando operação a realizar Deve possuir operação para calcular resultado da sua aplicação a dois operandos double 29 Introdução à Programação 2003/2004 TAD Operador /** Representa um operador aritmético, com o qual se podem realizar operações. @invariant ?. */ class Operador { public: … }; 30 Introdução à Programação 2003/2004 Construtor Operador::Operador() A utilização de explicit impede a conversão implícita de char em Operador. /** … */ class Operador { public: /** Constrói um novo operador correspondente ao símbolo dado. @pre símbolo pertence a {'+', '-', '*', '/', '^'}. @post Operador é o operador representado por símbolo. */ explicit Operador(char const símbolo); … }; Código possível: Operador multiplicação(‘*’); 31 Introdução à Programação 2003/2004 Operação Operador::operator()() /** … */ class Operador { public: Chiça! … /** Devolve o resultado do operador quando aplicado aos operandos dados. @pre *this ≠ Operador(‘/’) operando_direito ≠ 0. @post operator() = operando_esquerdo *this operando_direito. */ double operator()(double const operando_esquerdo, double const operando_direito) const; … }; O operador parênteses é usado na invocação de rotinas. É um operador n-ário, que pode ser sobrecarregado! Código possível: 32 Operador multiplicação(‘*’); cout << multiplicação(2.0, 3.0) << endl; Introdução à Programação 2003/2004 Operação Operador::operator<() /** … */ class Operador { public: … /** Verifica se o operador tem menor precedência que o operador dado. @pre V. @post operador< = (precedência de *this é menor que a de outro_operador). */ bool operator<(Operador const& outro_operador) const; … }; Código possível: Operador multiplicação(‘*’); Operador adição(‘+’); if(adição < multiplicação) … 33 Introdução à Programação 2003/2004 Operação Operador::operator==() /** … */ class Operador { public: … /** Verifica se o operador é igual ao operador dado. Note-se que a >= b e b >= a não implica que a == b, mas sim que a e b têm a mesma precedência. @pre V. @post operador== = (*this representa o mesmo operador que outro_operador). */ bool operator==(Operador const& outro_operador) const; … Código possível: }; Operador multiplicação(‘*’); Operador adição(‘+’); if(adição == multiplicação) … 34 Introdução à Programação 2003/2004 Operação Operador::símboloÉVálido() Um membro qualificado com static diz-se de /** … */ class Operador { public: … classe. Se for uma operação, não tem instância implícita. As únicas diferenças para uma rotina não-membro são os privilégios de acesso a membros privados e o nome qualificado com o nome da classe. /** Verifica se um símbolo é representação de algum dos operadores suportados. @pre V. @post símbolo pertence a {'+', '-', '*', '/', '^'}. */ static bool símboloÉVálido(char const símbolo); … Código possível: }; 35 char símbolo; cin >> símbolo; if(not Operador::símboloÉVálido(símbolo)) cout << “…” << endl; else { Operador operador(símbolo); … } Introdução à Programação 2003/2004 TAD Operador: Implementação /** Representa um operador aritmético, com o qual se podem realizar operações. @invariant símbolo pertence a {'+', '-', '*', '/', '^'}. */ class Operador { … private: /** Indica se o invariante da classe é cumprido. @pre V. @post símbolo pertence a {'+', '-', '*', '/', '^'}. */ bool cumpreInvariante() const; char símbolo; }; 36 Introdução à Programação 2003/2004 Construtor Operador::Operador() inline Operador::Operador(char const símbolo) : símbolo(símbolo) { assert(símboloÉVálido(símbolo)); assert(cumpreInvariante()); } 37 Introdução à Programação 2003/2004 Método Operador::operator()() double Operador::operator()(double const operando_esquerdo, double const operando_direito) const { assert(cumpreInvariante()); assert(símbolo != '/' or operando_direito != 0); switch(símbolo) { case '+': return operando_esquerdo + operando_direito; case '-': return operando_esquerdo - operando_direito; case '*': return operando_esquerdo * operando_direito; case '/': return operando_esquerdo / operando_direito; case '^': return pow(operando_esquerdo, operando_direito); } } 38 Introdução à Programação 2003/2004 Método Operador::operator<() inline bool Operador::operator<(Operador const& outro_operador) const { assert(cumpreInvariante()); switch(símbolo) { case '+': case '-': return outro_operador.símbolo outro_operador.símbolo == outro_operador.símbolo == case '*': case '/': return outro_operador.símbolo case '^': return false; } == '*' or '/' or '^'; == '^'; } 39 Introdução à Programação 2003/2004 Método Operador::operator==() inline bool Operador::operator==(Operador const& outro_operador) const { assert(cumpreInvariante()); return símbolo == outro_operador.símbolo; } 40 Introdução à Programação 2003/2004 Método Operador::símboloÉVálido() inline bool Operador::símboloÉVálido(char const símbolo) { return símbolo == '+' or símbolo == '-' or símbolo == '*' or símbolo == '/' or símbolo == '^'; } 41 Introdução à Programação 2003/2004 Método Operador::cumpreInvariante() inline bool Operador::cumpreInvariante() const { return símboloÉVálido(símbolo); } 42 Introdução à Programação 2003/2004 TAD Calculadora Representa uma calculadora Para ser mais curto e legível, o código não verifica nenhum possível erro do utilizador! 43 Introdução à Programação 2003/2004 TAD Calculadora /** Representa uma calculadora que opera sobre expressões lidas do canal cin, escrevendo o resultado em cout. As expressões podem envolver os operadores binários +, -, *, / e ^ (potenciação). Não podem incluir parênteses. As expressões são separadas por ; (ponto-e-vírgula) e terminadas por . (ponto). @invariant ?. */ class Calculadora { public: … }; 44 Introdução à Programação 2003/2004 Operação Calculadora::executa() /** … */ class Calculadora { public: /** Executa a calculadora sobre expressões presentes no canal cin. @pre As expressões no canal cin têm de estar sintacticamente correctas, estar separadas por ; (ponto-e-vírgula) e a sua sequência deve ser terminada por . (ponto). @post O canal cout contém os resultados de todas as expressões, um em cada linha. */ void executa(); … }; 45 Introdução à Programação 2003/2004 TAD Calculadora: Implementação /** Representa uma calculadora que opera sobre expressões lidas do canal cin, escrevendo o resultado em cout. As expressões podem envolver os operadores binários +, -, *, / e ^ (potenciação). Não podem incluir parênteses. As expressões são separadas por ; (ponto-e-vírgula) e terminadas por . (ponto). @invariant pilha_de_valores.empty() pilha_de_operadores.empty(). */ class Calculadora { … private: … /** Indica se o invariante da classe é cumprido. @pre V. @post pilha_de_valores.estáVazia() pilha_de_operadores. estáVazia(). */ bool cumpreInvariante() const; }; 46 PilhaDeDouble pilha_de_valores; PilhaDeOperador pilha_de_operadores; Introdução à Programação 2003/2004 Operação Calculadora:: efectuaOperaçãoNoTopo() /** … */ class Calculadora { … private: /** Efectua operação correspondente ao operador que se encontra no topo da pilha de operadores usando como operandos os dois valores no topo da pilha de valores. O valor que se encontra no topo da pilha de valores é o operando direito, sendo o valor imediatamente abaixo o operando esquerdo. @pre 1 ≤ pilha_de_operadores.altura() 2 ≤ pilha_de_valores.altura(). @post Pilha de valores deixou de conter os dois valores do topo, tendo sido colocado no lugar deles o resultado do operador do topo da pilha de operadores (que entretanto foi retirado) quando aplicado aos dois valores retirados (o primeiro valor retirado da pilha de valores é o operando direito). */ void efectuaOperaçãoNoTopo(); … }; 47 Introdução à Programação 2003/2004 Operação Calculadora:: efectuaOperacoesDePrecedenciaMaiorOuIgualA() /** … */ class Calculadora { … private: … /** Efectua os cálculos de todos os operadores de precedência superior ou igual ao operador passado como argumento. @pre n = pilha_de_operadores.altura() k = número de operadores na pilha de operadores com precedência superior ou igual a operador m = pilha_de_valores.altura() k + 1 ≤ m. @post pilha_de_operadores.altura() = n – k pilha_de_valores.altura() = m – k – 1. */ void efectuaOperaçõesDePrecedênciaMaiorOuIgualA( Operador const& operador); … }; 48 Introdução à Programação 2003/2004 Operação Calculadora:: efectuaTodasAsOperações() /** … */ class Calculadora { … private: … /** Efectua os cálculos de todos os operadores que estão na pilha. @pre n = pilha_de_operadores.altura() m = pilha_de_valores.altura() n + 1 <= m. @post pilha_de_operadores.estáVazia() pilha_de_valores.altura() = m – n – 1. */ void efectuaTodasAsOperações(); … }; 49 Introdução à Programação 2003/2004 Método Calculadora::executa() void Calculadora::executa() { assert(cumpreInvariante()); char caractere; do { cin >> caractere; if(not cin.fail()) if(Operador::simboloÉVálido(caractere)) { // Processamento de operador. } else if(caractere == ';' or caractere == '.') { // Processamento de terminador. } else if(isdigit(caractere)) { // Processamento de valor. } } while(not cin.fail() and caractere != '.'); assert(cumpreInvariante()); } 50 Introdução à Programação 2003/2004 Método Calculadora::executa() … if(Operador::simboloÉVálido(caractere)) { efectuaOperaçõesDePrecedênciaMaiorOuIgualA(Operador(caractere)); pilha_de_operadores.põe(Operador(caractere)); } else if(caractere == ';' or caractere == '.') { efectuaTodasAsOperações(); cout << pilha_de_valores.itemNoTopo() << endl; pilha_de_valores.tiraItem(); } else if(isdigit(caractere)) { cin.putback(caractere); double valor; cin >> valor; pilha_de_valores.põe(valor); } … 51 Introdução à Programação 2003/2004 Método Calculadora:: efectuaOperaçãoNoTopo() void Calculadora::efectuaOperaçãoNoTopo() { assert(1 <= pilha_de_operadores.altura()); assert(2 <= pilha_de_valores.altura()); double operando_direito = pilha_de_valores.itemNoTopo(); pilha_de_valores.tiraItem(); double operando_esquerdo = pilha_de_valores.itemNoTopo(); pilha_de_valores.tiraItem(); Operador operador = pilha_de_operadores.itemNoTopo(); pilha_de_operadores.tiraItem(); pilha_de_valores.põe(operador(operando_esquerdo, operando_direito)); } 52 Introdução à Programação 2003/2004 Método Calculadora:: efectuaOperacoesDeMaiorPrecedenciaQue() void Calculadora::efectuaOperaçõesDePrecedênciaMaiorOuIgualA( Operador const& operador) { while(not pilha_de_operadores.estáVazia() and pilha_de_operadores.itemNoTopo() >= operador) efectuaOperaçãoNoTopo(); } 53 Introdução à Programação 2003/2004 Método Calculadora:: efectuaTodasAsOperações() void Calculadora::efectuaTodasAsOperações() { while(not pilha_de_operadores.estáVazia()) efectuaOperaçãoNoTopo(); assert(pilha_de_operadores.estáVazia()); } 54 Introdução à Programação 2003/2004 Método Calculadora:: cumpreInvariante() bool Calculadora::cumpreInvariante() const { return pilha_de_valores.estáVazia() and pilha_de_operadores.estáVazia(); } 55 Introdução à Programação 2003/2004 E as pilhas? Programa escrito usa-as Necessário desenvolvê-las Desenho de TAD será demonstrado com as pilhas 56 Introdução à Programação 2003/2004 Desenho de TAD Recomendável começar por escrever restante código assumindo que existem Recomendável também escrever programa de teste Ambos clarificam operações e comportamentos desejáveis, bem como a interface desejável 57 Introdução à Programação 2003/2004 Operações das pilhas Começaremos por PilhaDeDouble 58 Construir pilha vazia Verificar se está vazia Obter o item do topo Saber altura da pilha Pôr item na pilha Tirar o item do topo Introdução à Programação construtores predicados (inspectores) inspectores modificadores 2003/2004 TAD PilhaDeDouble: Teste de unidade int main() { PilhaDeDouble pilha; assert(pilha.estáVazia()); assert(pilha.altura() == 0); for(int i = 0; i != 10; ++i) { assert(pilha.altura() == i); pilha.poe(i); assert(pilha.itemNoTopo() == i); assert(not pilha.estáVazia()); assert(pilha.altura() == i + 1); } (continua) 59 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Teste de unidade (continuação) assert(pilha.itemNoTopo() == 9); assert(not pilha.estáVazia()); assert(pilha.altura() == 10); for(int i = 9; i != -1; --i) { assert(pilha.itemNoTopo() == i); assert(not pilha.estáVazia()); assert(pilha.altura() == i + 1); pilha.tiraItem(); assert(pilha.altura() == i); } assert(pilha.estáVazia()); assert(pilha.altura() == 0); } 60 Introdução à Programação 2003/2004 TAD PilhaDeDouble /** Representa pilhas de double. @invariant [o invariante é um problema de implementação]. */ class PilhaDeDouble { public: typedef double Item; … }; Permite alterar o tipo dos itens com muita facilidade. Definir PilhaDeOperador torna-se trivial: a) Copiar classe PilhaDeDouble completa. b) Substituir PilhaDeDouble por PilhaDeOperador. c) Alterar definição de Item de double para Operador. 61 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Construtores /** … */ class PilhaDeDouble { public: … /** Constrói pilha vazia. @pre V. @post estáVazia(). */ PilhaDeDouble(); … }; 62 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Predicados /** … */ class PilhaDeDouble { public: … /** Indica se a pilha está vazia. @pre V. @post estáVazia = *this está vazia. */ bool estáVazia() const; … }; 63 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Inspectores /** … */ class PilhaDeDouble { public: … /** Devolve o item que está no topo da pilha. @pre ¬estáVazia(). @post itemNoTopo idêntico ao item no topo de *this. */ Item const& itemNoTopo() const; /** Devolve altura da pilha. @pre V. @post altura = altura de *this (número de itens). */ int altura() const; … }; 64 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Modificadores /** … */ class PilhaDeDouble { public: … /** Põe um novo item na pilha (no topo). @pre V. @post *this contém um item adicional no topo igual a novo_item. */ void põe(Item const& novo_item); /** Tira o item que está no topo da pilha. @pre ¬estáVazia(). @post *this contém os itens originais menos o do topo. */ void tiraItem(); private: … }; 65 Introdução à Programação 2003/2004 TAD PilhaDeDouble: Implementação 66 Fica como exercício Introdução à Programação 2003/2004 Estrutura global do programa #include #include #include #include #include #include <iostream> <utility> <cassert> <cctype> <cmath> … // define <=, >, >= e != à custa de < e de ==. // para isdigit(). // para pow(). // outras inclusões necessárias para as pilhas. using namespace std; using namespace std::rel_ops; // para definições de <= etc. terem efeito. class Operador {…} // e respectivos métodos. class PilhaDeDouble {…} // e respectivos métodos. class PilhaDeOperador {…} // e respectivos métodos. class Calculadora {…} // e respectivos métodos. (continua) 67 Introdução à Programação 2003/2004 Estrutura global do programa (continuação) #ifdef TESTE int main() { // Testes das várias classes! } Para compilar os testes usa-se um comando de compilação semelhante a: c++ -DTESTE … #else // TESTE int main() { Calculadora calculadora; calculadora.executa(); } #endif // TESTE 68 Introdução à Programação 2003/2004 Programação procedimental Primeiro pensa-se nos algoritmos Depois pensa-se nos dados 69 Introdução à Programação 2003/2004 Programação baseada em objectos Primeiro pensa-se nos dados (TAD) Depois pensa-se nos algoritmos Frequentemente são os substantivos mais importantes que surgem na descrição do problema. 70 Introdução à Programação 2003/2004 Desenho de TAD TAD são módulos Designers e departamento marketing definem interface Engenharia desenvolve o mecanismo 71 Introdução à Programação 2003/2004 Desenho de TAD Surgem para auxiliar resolução de problema Resolução usada para sugerir operações necessárias no TAD Começa-se pelo teste de unidade Teste de unidade testa todas as operações do TAD Depois desenha-se a interface do TAD Finalmente implementa-se: 72 Passo a passo, testando a cada passo Se necessário fazer implementação embrionária de métodos e rotinas (apenas instruções asserção e nas funções uma instrução de retorno). Introdução à Programação 2003/2004 Alerta! Há pilhas na biblioteca do C++: #include <stack> #include <iostream> using namespace std; int main() { stack<int> pilha; pilha.push(1); pilha.push(2); while(not pilha.empty()) { cout << pilha.top() << endl; pilha.pop(); } } 73 Introdução à Programação 2003/2004 Aula 14: Sumário Desenho de TAD: programação baseada em objectos Fases: Exemplo com pilhas. Definição de sinónimos de tipos com typedef Classe genérica stack Noções elementares de eXtreme Programming: 74 Escrita de testes Declaração das operações suportadas, suas consequências, e forma de utilização: interface Definição dos atributos (representação) e dos métodos (mecanismo): implementação. Testes primeiro Desenvolvimento incremental Introdução à Programação 2003/2004