Aula 14 Programação Baseada em Objectos Desenho de TAD.
Transcript of Aula 14 Programação Baseada em Objectos Desenho de TAD.
Aula 14
Programação Baseada em Objectos
Desenho de TAD
2003/2004Introdução à Programação2
Desenvolver uma calculadora
Operações:+ – adição- – subtracção/ – divisão* – multiplicação^ – potenciação
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
2003/2004Introdução à Programação3
Exemplo
Entrada:
2 + 2 ;2 * 2.5 .
Saída:
45
2003/2004Introdução à Programação4
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)
2003/2004Introdução à Programação5
Cálculo de expressão
Símbololido:
1
2003/2004Introdução à Programação6
Cálculo de expressão
Que fazer aos símbolos lidos? Ao 1 e ao +?
É necessário guardá-los algures!
Símbololido:
+
2003/2004Introdução à Programação7
Cálculo de expressão
Podemos calcular já 1 + 4?
Não! Depende do que se segue!
Símbololido:
4
2003/2004Introdução à Programação8
Cálculo de expressão
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
Símbololido:
*
Memorizar… No nosso programa teremos de guardar os símbolos algures!
2003/2004Introdução à Programação9
Cálculo de expressão
E a saga continuaSímbololido:
3
2003/2004Introdução à Programação10
Cálculo de expressão
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:
Ao ler um operador, podemos calcular os operadores em espera enquanto forem de precedência maior ou igual
Símbololido:
/
Por ordem inversa à da leitura!
Podem-se empilhar os operadores!
2003/2004Introdução à Programação11
Cálculo de expressão
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
Símbololido:
2
Também se podem empilhar os valores!
2003/2004Introdução à Programação12
Regressemos ao início
Vamos recomeçar o cálculo, mas equipando-nos com duas pilhas:
Pilha dos operadores
Pilha dos valores
2003/2004Introdução à Programação13
valoresoperadores
Cálculo de expressão
Lido um valor, há que colocá-lo na respectiva pilha
Símbololido: 1
2003/2004Introdução à Programação14
valoresoperadores
Cálculo de expressão
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
Símbololido: +
1
2003/2004Introdução à Programação15
valoresoperadores
Cálculo de expressão
Valores vão sempre para a pilha
Símbololido: 4
1+
2003/2004Introdução à Programação16
valoresoperadores
Cálculo de expressã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
Símbololido: *
1+
4
2003/2004Introdução à Programação17
valoresoperadores
Cálculo de expressão
É um valor…Símbololido: 3
1+
4*
2003/2004Introdução à Programação18
valoresoperadores
Cálculo de expressão
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
Símbololido: /
1+
4*
3
= 12
2003/2004Introdução à Programação19
valoresoperadores
Cálculo de expressão
Valores vão directos para a pilha, como sempre
Símbololido: 2
1+
12/
2003/2004Introdução à Programação20
valoresoperadores
Cálculo de expressã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
Símbololido: ^
1+
12/
2
2003/2004Introdução à Programação21
valoresoperadores
Cálculo de expressão
Pilha com ele!Símbololido: 3
1+
12/
2^
2003/2004Introdução à Programação22
valoresoperadores
Cálculo de expressão
Têm de se realizar todas as operações com precedência maior ou igual!
Símbololido: -
1+
12/
2^
3
= 8
2003/2004Introdução à Programação23
valoresoperadores
Cálculo de expressão
Têm de se realizar todas as operações com precedência maior ou igual!
Símbololido: -
1+
12/
8
= 1,5
2003/2004Introdução à Programação24
valoresoperadores
Cálculo de expressão
Têm de se realizar todas as operações com precedência maior ou igual!
Depois, guarda-se a operação na pilha
Símbololido: -
1+
1,5 = 2,5
2003/2004Introdução à Programação25
valoresoperadores
Cálculo de expressão
Mais um…Símbololido: 2
2,5-
2003/2004Introdução à Programação26
valoresoperadores
Cálculo de expressão
Ao atingir o símbolo . (ponto), efectuam-se todas as operações que restam na pilha
Símbololido: .
2,5-
2 = 0,5
2003/2004Introdução à Programação27
valoresoperadores
Cálculo de expressão
Ao atingir o símbolo . (ponto), efectuam-se todas as operações que restam na pilha
O resultado retira-se da pilha dos valores:
Símbololido:
0,5
2003/2004Introdução à Programação28
E agora?
É necessário escrever o programa…
Mas já se sabe que ferramentas estão em falta: Um TAD para representar operadores Um TAD para representar uma calculadora E…
2003/2004Introdução à Programação29
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
2003/2004Introdução à Programação30
TAD Operador
/** Representa um operador aritmético, com o qual se podem realizar operações. @invariant ?. */ class Operador { public: …
};
2003/2004Introdução à Programação31
Construtor Operador::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(‘*’);
A utilização de explicit impede a conversão implícita de char em Operador.
2003/2004Introdução à Programação32
Operação Operador::operator()()/** … */ class Operador { public:
…
/** 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:
Operador multiplicação(‘*’);cout << multiplicação(2.0, 3.0) << endl;
Chiça!
2003/2004Introdução à Programação33
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) …
2003/2004Introdução à Programação34
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) …
2003/2004Introdução à Programação35
Operação Operador::símboloÉVálido()
/** … */ class Operador { public:
…
/** 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:
char símbolo;cin >> símbolo;if(not Operador::símboloÉVálido(símbolo)) cout << “…” << endl;else { Operador operador(símbolo); …}
Um membro qualificado com static diz-se de 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.
2003/2004Introdução à Programação36
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;};
2003/2004Introdução à Programação37
Construtor Operador::Operador()inline Operador::Operador(char const símbolo) : símbolo(símbolo) { assert(símboloÉVálido(símbolo));
assert(cumpreInvariante()); }
2003/2004Introdução à Programação38
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); } }
2003/2004Introdução à Programação39
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 == '*' or outro_operador.símbolo == '/' or outro_operador.símbolo == '^'; case '*': case '/': return outro_operador.símbolo == '^'; case '^': return false; } }
2003/2004Introdução à Programação40
Método Operador::operator==()inline bool Operador::operator==(Operador const& outro_operador) const { assert(cumpreInvariante());
return símbolo == outro_operador.símbolo;}
2003/2004Introdução à Programação41
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 == '^'; }
2003/2004Introdução à Programação42
Método Operador::cumpreInvariante()
inline bool Operador::cumpreInvariante() const { return símboloÉVálido(símbolo); }
2003/2004Introdução à Programação43
TAD Calculadora
Representa uma calculadora
Para ser mais curto e legível, o código não verifica nenhum possível erro do utilizador!
2003/2004Introdução à Programação44
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: …
};
2003/2004Introdução à Programação45
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();
…
};
2003/2004Introdução à Programação46
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;
PilhaDeDouble pilha_de_valores; PilhaDeOperador pilha_de_operadores; };
2003/2004Introdução à Programação47
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();
…};
2003/2004Introdução à Programação48
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);
…};
2003/2004Introdução à Programação49
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();
…
};
2003/2004Introdução à Programação50
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()); }
2003/2004Introdução à Programação51
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); }
…
2003/2004Introdução à Programação52
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)); }
2003/2004Introdução à Programação53
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();}
2003/2004Introdução à Programação54
Método Calculadora::efectuaTodasAsOperações()void Calculadora::efectuaTodasAsOperações(){ while(not pilha_de_operadores.estáVazia()) efectuaOperaçãoNoTopo(); assert(pilha_de_operadores.estáVazia());}
2003/2004Introdução à Programação55
Método Calculadora::cumpreInvariante()bool Calculadora::cumpreInvariante() const { return pilha_de_valores.estáVazia() and pilha_de_operadores.estáVazia(); }
2003/2004Introdução à Programação56
E as pilhas?
Programa escrito usa-as
Necessário desenvolvê-las
Desenho de TAD será demonstrado com as pilhas
2003/2004Introdução à Programação57
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
2003/2004Introdução à Programação58
Operações das pilhas
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
construtores
predicados (inspectores)
inspectores
modificadores
Começaremos por PilhaDeDouble
2003/2004Introdução à Programação59
TAD PilhaDeDouble: Teste de unidadeint 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)
2003/2004Introdução à Programação60
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);}
2003/2004Introdução à Programação61
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.
2003/2004Introdução à Programação62
TAD PilhaDeDouble: Construtores/** … */ class PilhaDeDouble { public:
…
/** Constrói pilha vazia.
@pre V.
@post estáVazia(). */ PilhaDeDouble();
…
};
2003/2004Introdução à Programação63
TAD PilhaDeDouble: Predicados/** … */ class PilhaDeDouble { public:
…
/** Indica se a pilha está vazia.
@pre V.
@post estáVazia = *this está vazia. */
bool estáVazia() const;
…
};
2003/2004Introdução à Programação64
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; …
};
2003/2004Introdução à Programação65
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: …};
2003/2004Introdução à Programação66
TAD PilhaDeDouble: Implementação
Fica como exercício
2003/2004Introdução à Programação67
Estrutura global do programa
#include <iostream>#include <utility> // define <=, >, >= e != à custa de < e de ==. #include <cassert> #include <cctype> // para isdigit(). #include <cmath> // para pow(). #include … // 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)
2003/2004Introdução à Programação68
Estrutura global do programa
(continuação)
#ifdef TESTE
int main(){ // Testes das várias classes!}
#else // TESTE
int main() { Calculadora calculadora;
calculadora.executa(); }
#endif // TESTE
Para compilar os testes usa-se um comando de compilação semelhante a:
c++ -DTESTE …
2003/2004Introdução à Programação69
Programação procedimental
Primeiro pensa-se nos algoritmos
Depois pensa-se nos dados
2003/2004Introdução à Programação70
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.
2003/2004Introdução à Programação71
Desenho de TAD
TAD são módulos
Designers e departamento marketing definem interface
Engenharia desenvolve o mecanismo
2003/2004Introdução à Programação72
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:
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).
2003/2004Introdução à Programação73
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(); }}
2003/2004Introdução à Programação74
Aula 14: Sumário
Desenho de TAD: programação baseada em objectos Fases:
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. Exemplo com pilhas. Definição de sinónimos de tipos com typedef Classe genérica stack Noções elementares de eXtreme Programming:
Testes primeiro Desenvolvimento incremental