• Nenhum resultado encontrado

PCS LABORATÓRIO DE PROGRAMAÇÃO ORIENTADA A OBJETOS PARA A ENGENHARIA ELÉTRICA

N/A
N/A
Protected

Academic year: 2021

Share "PCS LABORATÓRIO DE PROGRAMAÇÃO ORIENTADA A OBJETOS PARA A ENGENHARIA ELÉTRICA"

Copied!
10
0
0

Texto

(1)

ESCOLA POLITÉCNICA DA UNIVERSIDADE DE SÃO PAULO

Departamento de Engenharia de Computação e Sistemas Digitais

PCS

3111

-

L

ABORATÓRIO DE

P

ROGRAMAÇÃO

O

RIENTADA A

O

BJETOS PARA A

E

NGENHARIA

E

LÉTRICA

E

XERCÍCIO

I

NTEGRADOR

P

ARTE

2

2014

Resumo

A segunda parte do exercício integrador tem como objetivo usar o conceito de grafo em um software orientado a objetos. Deve-se criar uma nova funcionalidade no sistema para permitir traçar a rota entre um espaço e outro, considerando uma configuração de espaços obtida em um arquivo texto.

Objetivos

 Usar o conceito de grafos em um software orientado a objetos.  Acessar um arquivo de configuração.

 Usar os demais containers da STL.

1 Introdução

Usando o projeto que foi implementado na aula anterior (Aula 10), deve-se acrescentar uma funcionalidade para traçar uma rota entre dois espaços. Tal rota poderia ser útil, por exemplo, para que um entregador de um novo equipamento pudesse colocá-lo de modo conveniente no Espaço desejado. Para criar tal rota, será usado um grafo para representar a relação entre Espaços em um Local.

1.1

Fundamentação teórica

A seguir será apresentada uma breve explicação sobre grafos e busca em largura. Sugere-se a consulta dos livros do Cormen et al. [1] e do Johnsonbaugh [2] para maiores informações sobre grafos.

1.1.1 Grafos

Grafos são abstrações usadas para modelar problemas de várias áreas como, por exemplo, Engenharia, Computação, Biologia, Economia e Sociologia. Um grafo é definido como um par (V, E), onde V representa um conjunto de vértices (ou nós) e E é um conjunto de arestas (ou arcos). Cada aresta representa um par (v1, v2), com v1 e v2 V.

Existem diversas maneiras de se representar um grafo computacionalmente. De uma forma geral, um grafo pode ser representado como uma matriz de adjacência ou uma lista de adjacência1. Especificamente sobre uma lista de adjacência (que será usada neste exercício), para representar um grafo usa-se um arranjo em que cada posição representa uma lista de vértices adjacentes. Por exemplo, considere um grafo não orientado G = (V, E) definido como:

1

(2)

V = {1, 2, 3, 4}

E = {(1, 2), (1, 3), (2, 3), (2, 4), (3, 4)}

Esse grafo é representado graficamente na Figura 1a e através de uma lista de adjacência na Figura 1b. A lista de adjacências usa um vetor de tamanho |V| em que cada posição é uma lista ligada.

(a) (b)

Figura 1: Um exemplo de um grafo (a) e a representação dele como uma lista de adjacência (b).

1.2

Busca em largura

Existem dois algoritmos básicos para varrer um grafo: a busca em largura e a busca em profundidade. Na busca em largura, primeiro visita-se todos os vértices conectados a um vértice antes de visitar outros vértices. Por exemplo, no caso do grafo apresentado na Figura 1a, a busca em largura a partir do vértice 1 visitaria todos os vértices ligados a 1, ou seja, os vértices 2 e 3 e só então analisaria os vértices conectados ao vértice 2 e ao vértice 3 (no caso o vértice 4).

Um algoritmo para busca em largura (BFS) é apresentado a seguir, baseado no definido em [1]. BFS (G, s)

for each vértice u ϵ G.V – {s} u.cor = BRANCO u.dist = ∞ u.pred = NIL s.cor = CINZA s.dist = 0 s.pred = NIL Q = Ø Enqueue(Q, s) while Q != Ø u = Dequeue(Q)

for each v ϵ G.Adjacencia[u] if v.cor == BRANCO v.cor = CINZA v.dist = u.dist + 1 v.pred = u Enqueue(Q,v) u.cor = PRETO

Esse algoritmo usa a ideia de cor para saber se um vértice já foi visitado ou não (os brancos não foram visitados, enquanto que os cinza estão na fila para serem analizados e os pretos já foram analisados). Para se saber a rota entre um vértice e a origem é possível usar o atributo predecessor (pred).

1.3

Implementação de grafos em C++

A seguir será apresentado como implementar um grafo em C++, considerando o projeto da disciplina.

1 3 2 4 1 2 3 4 2 2 1 1 3 / 3 2 3 / 4 / 4 /

(3)

1.3.1 Lista de adjacência em C++

Uma forma simples de representar uma lista de adjacência é fazer um vetor de listas, com um tamanho igual ao número de vértices. Com isso, em cada posição há uma lista. Usando a biblioteca STL do C++, pode-se usar o container std::list como implementação de uma lista ligada:

// Um array de listas de Vértices

std::list<Vertice*> listaDeAdjacencia[NUMERO_DE_VERTICES];

// Um vector de listas de Vértices

std::vector<std::list<Vertice*>> listaDeAdjacencia;

Uma dificuldade dessa implementação é que é preciso realizar um mapeamento manual entre uma posição do vetor e um Vértice; ou seja, é preciso saber que um Vértice va está na posição 0 do vetor e que

um Vértice vb está na posição 1 do vetor, por exemplo. Uma forma simples de evitar isso é usar um mapa,

disponível na STL em std::map. Mapas armazenam elementos na forma de pares chave e valor. A partir de uma chave, acessa-se o valor associado a ela. Em C++, a chave e o valor podem ser de qualquer tipo. No nosso exemplo, a chave seria um vértice e o valor seria uma lista de vértices. Isso poderia ser declarado como:

std::map<Vertice*, std::list<Vertice*>> listaDeAdjacencia;

Usando essa implementação, pode-se conectar um Vértice a outro em um grafo não orientado da seguinte forma:

Vértice *v1 = ...; Vértice *v2 = ...;

listaDeAdjacencia[v1].push_back(v2); listaDeAdjacencia[v2].push_back(v1); 1.3.2 Local como um grafo

Um Local do sistema de automação residencial possui diversos Espaços, conforme apresentado no diagrama de classes do Apêndice A. Na implementação, um vector é usado para armazenar uma lista de Espaços. Porém, essa solução não permite representar as relações entre vários Espaços.

Por exemplo, considere a planta de uma casa apresentada na Figura 2. A cozinha, a sala, a suíte, etc. são espaços e as portas são representadas por um retângulo preto. Dessa forma, existe uma porta entre a cozinha e a sala e entre a sala de jantar e a cozinha, por exemplo. Essa relação entre os Espaços pode ser representada como um grafo: cada Espaço é um vértice e cada porta é uma aresta, ligando dois Espaços. O grafo representando a planta da Figura 2 é apresentado na Figura 3.

Figura 2: Planta de uma casa.

Sala de Jantar Cozinha Sala Quarto1 Quarto2 Suíte Banheiro Banheiro da Suíte Corredor

(4)

Figura 3: Grafo representando a ligação entre Espaços da Figura 2.

1.3.3 Busca em largura em C++

Em uma solução OO, um vértice é um objeto. Na implementação do procedimento BFS, seria necessário criar novos atributos para implementar a cor, a distância e o predecessor. Porém, isso pode não ser adequado já que geram-se atributos que não fazem sentido para o tipo em questão. Por exemplo, esses atributos não fazem sentido para a classe Espaço do nosso sistema. Uma forma de resolver este problema é usar uma ou mais estruturas adicionais para representar as informações necessárias para executar o procedimento.

Ao analisar quais informações são realmente necessárias, é possível simplificar a estrutura necessária para executar o algoritmo. A cor, por exemplo, pode ser facilmente retirada ao usar o predecessor (se o predecessor for NIL significa que o vértice ainda não foi visitado). Se a distância também não for relevante (como não será no nosso projeto), só será necessária uma estrutura para guardar o predecessor de um determinado Espaço.

Para implementar uma Fila em C++ pode-se usar o container std::queue. Por fim, para imprimir um caminho é possível usar o predecessor, indo de trás para frente (do destino à origem).

2. Atividades

2.1

Entrega 0 (já feito em casa)

 Espaço

o Altere a implementação para usar um vector ao invés de um arranjo de Equipamentos (Equipamento[]). Sala de Jantar Sala Suíte Banheiro da Suíte Quarto1 Quarto2 Banheiro Corredor Cozinha

(5)

2.2 Entrega 1

 Mapa

o Crie uma classe Mapa que permite representar as relações entre os Espaços como um Grafo. Essa classe deve ter a seguinte definição:

class Mapa { public: Mapa();

void adicionarEspaco(Espaco* e);

void conectar(Espaco* e1, Espaco* e2);

void imprimirCaminho(Espaco* origem, Espaco* destino); private:

std::vector<Espaco*> espacos;

std::map<Espaco*,std::list<Espaco*>> listaDeAdjacencia; };

 O método adicionarEspaço deve adicionar o Espaço e ao Mapa.

 O método conectar deve criar uma aresta entre o Espaco e1 e o Espaco e2 (e vice-versa).

 O método imprimirCaminho deve usar o algoritmo de busca em largura para imprimir o caminho entre a origem e o destino.

Nota1: Agora, há dois “espacos”: Local::espacos e Mapa::espacos. Em condições normais, os dois atributos devem ser iguais.

Nota2: conectar(e1,e2) deve criar duas arestas: e1->e2 E e2->e1.

Nota3: Possivelmente, a solução ficaria mais simples se trabalhasse com índices em vetor “espacos” em vez de ponteiros para Espaco.

Nota4: É importante ter como testar se o programa está correto. Faça funcionar a seguinte main.cpp, usando casa.txt: ... try { LeitorDeConfiguracao leitor; Local *local; string arquivo;

cout << "Informe o arquivo de configuracao ou aperte ENTER para usar o padrao" << endl; getline(cin, arquivo);

cout << endl;

if (arquivo == "") local = leitor.carregar(); else local = leitor.carregar(arquivo);

Mapa *mapa=new Mapa;

Painel painelCentral(local); // Aula10

Espaco *e0=local->getEspaco(0); cout << e0->getNome() << endl; Espaco *e1=local->getEspaco(1); cout << e1->getNome() << endl; Espaco *e2=local->getEspaco(2); cout << e2->getNome() << endl; Espaco *e3=local->getEspaco(3);

(6)

cout << endl; mapa->adicionarEspaco( e0 ); mapa->adicionarEspaco( e1 ); mapa->adicionarEspaco( e2 ); mapa->adicionarEspaco( e3 ); mapa->conectar( e0, e2 ); mapa->conectar( e1, e2 ); mapa->conectar( e2, e3 ); mapa->imprimirCaminho(e0,e1); mapa->imprimirCaminho(e0,e3); delete local; delete mapa;

} catch (ErroDeArquivo erro) { ... Saída: Sala SalaDeJantar Corredor Quarto1 Sala->Corredor->SalaDeJantar Sala->Corredor->Quarto1

(7)

2.3 Entrega 2

 Crie uma classe LeitorDoMapa que lê um arquivo de configuração. Caso ao ler o arquivo se encontre um Espaco inválido, deve-se jogar um ErroDeArquivo.

o Essa classe deve possuir a seguinte definição: class LeitorDoMapa {

public:

LeitorDoMapa(Local* local); Mapa* carregar();

Mapa* carregar(std::string arquivo); private:

Espaco* encontrar(std::string nome); static const std::string ARQUIVO_PADRAO; Local *local;

};

 O método carregar deve criar um mapa considerando o local e o arquivo de configuração (o método sem parâmetros usa o ARQUIVO_PADRAO).

 O método encontrar é um método auxiliar para encontrar um Espaco a partir de seu nome. Onde? No Local::espacos?

 Use como ARQUIVO_PADRAO o arquivo “mapa.txt”. o O formato do arquivo de configuração deve ser o seguinte:

<Número de Espacos> <Nome do Espaco A> <Nome do Espaco B> ...

<Número de arestas>

<Nome do Espaco A> <Nome do Espaco B> <Nome do Espaco C> <Nome do Espaco A> ...

O arquivo “mapa.txt” apresenta um exemplo desse arquivo considerando o local descrito em “casa.txt”.

 Caso um Espaço do arquivo de configuração não esteja no Local, jogue um ErroDeArquivo.

É importante ter como testar se o programa está correto. Para isso, primeiro, declare temporariamente todos os atributos do Mapa.h como públicos:

//private:

std::vector<Espaco*> espacos;

(8)

Faça funcionar a seguinte main.cpp, usando casa.txt e mapa.txt:

try {

LeitorDeConfiguracao leitor; Local *local;

string arquivo;

cout << "Arquivo de configuracao (ENTER para o padrao): "; getline(cin, arquivo);

cout << endl;

if (arquivo == "") local = leitor.carregar(); else local = leitor.carregar(arquivo);

LeitorDoMapa leitorDoMapa(local);

cout << "Arquivo com o mapa (ENTER o padrao): "; getline(cin, arquivo);

Mapa *mapa;

if (arquivo == "") mapa = leitorDoMapa.carregar(); else mapa = leitorDoMapa.carregar(arquivo);

cout << endl;

// Hae: Para poder testar,

// deixe Mapa::espacos e Mapa::listaDeAdjacencia // temporariamente como public.

for (int i=0; i<mapa->espacos.size(); i++) { Espaco *e=mapa->espacos[i];

cout << e->getNome() << " [conectado a:] "; list<Espaco*> l=mapa->listaDeAdjacencia[e]; for (auto p=l.begin(); p!=l.end(); p++) cout << (*p)->getNome() << " "; cout << endl; } cout << endl; delete local; delete mapa;

} catch (ErroDeArquivo erro) {

A saída deve ser:

Sala [conectado a:] Corredor Cozinha

SalaDeJantar [conectado a:] Corredor Cozinha

Corredor [conectado a:] Sala SalaDeJantar Quarto1 Quarto2 Banheiro Suite Quarto1 [conectado a:] Corredor

Quarto2 [conectado a:] Corredor Banheiro [conectado a:] Corredor

Suite [conectado a:] Corredor BanheiroSuite BanheiroSuite [conectado a:] Suite

(9)

2.4 Entrega 3

 Altere o main para carregar o Mapa usando a classe LeitorDoMapa. Pergunte ao usuário se ele quer usar o arquivo padrão ou informar o arquivo.

 Altere a classe Painel para permitir traçar uma rota entre dois Espaços. o Crie uma opção 5: “Traçar Rota”.

o Altere o construtor do Painel para receber também um Mapa.

o Para fazer com que o usuário selecione um Espaço, use o método selecionaEspaco. Você deverá fazer duas chamadas para esse método (para obter a origem e o destino). Aqui, deve voltar a colocar os atributos do Mapa.h como privados:

private:

std::vector<Espaco*> espacos;

std::map<Espaco*, std::list<Espaco*>> listaDeAdjacencia;

3 Dicas

 Use os arquivos .h entregues (Mapa.h e LeitorDoMapa.h).

 Use a classe LeitorDeConfiguração como base para criar o LeitorDoMapa.

 Para imprimir um caminho é possível usar o predecessor. Porém, se terá um caminho de trás para frente (do destino até a origem). Uma solução elegante é usar uma pilha (std::stack) para imprimir o caminho na ordem certa.

4 Bibliografia

[1] Cormen, T.; Leiserson, C.E.; Rivest, R.L.; Stein, C. Introduction to Algorithms. The MIT Press, 3rd ed. 2009. Capítulos 22 e Apêndice B.

(10)

Apêndice A – Diagrama de classes do projeto

ItemControlável(nome) ~ItemControlável() getNome() controlar() exibir() nome ItemControlável carregar() carregar(arquivo) ARQUIVO_PADRÃO LeitorDeConfiguração Local(nome) ~Local()

adicionarEspaco(nome, limiteInferior, limiteSuperior) controlar() exibir() getEspaco(numero) getQuantidadeDeEspacos() Local Painel(local) mostrar() menuEconomiaDeAgua() menuMaquinaDeLavarRoupa(maquina) Painel

Espaco(nome, limiteInferior, limiteSuperior) ~Espaco()

controlar() exibir()

getEquipamento(numero) getQuantidadeDeEquipamentos()

adicionarArCondicionado(nome, temperatura, velocidade) adicionarLampada(nome) adicionarVentilador(nome, velocidade) adicionarChuveiro(nome) adicionarMáquinaDeLavarRoupa(nome) quantidadeDeEquipamentos Espaco Sensor() ~Sensor() geId() alarmeAcionado() ler() exibir() id contador Sensor SensorDePresenca() alarmeAcionado() ler() exibir() presencaDetectada SensorDePresenca SensorDeTemperatura(limiteInferior, limiteSuperior) alarmeAcionado() ler() exibir() getTemperatura() getLimiteInferior() getLimiteSuperior() temperatura limiteInferior limiteSuperior SensorDeTemperatura sensorDeTemperatura 1 possui sensorDePresenca 1 possui espacos * Equipamento(nome) ~Equipamento() ligar() desligar() estaLigado() ligado Equipamento ErroDeArquivo ErroDeCadastro ErroDeControle EquipamentoDeVentilacao(

nome, velocidade, sensor) controlar() ligar() getVelocidade() getVelocidadeMaxima() aumentarVelocidade() diminuirVelocidade() velocidade velocidadeMaxima EquipamentoDeVentilacao Lampada(nome) controlar() exibir() Lampada

ArCondicionado(nome, temperatura, velocidade, sensor) exibir()

setTemperatura(temperatura) temperatura

ArCondicionado

Ventilador(nome, velocidade, sensor) exibir() Ventilador sensor 1 monitora 1 local controla Chuveiro MáquinaDeLavarRoupa sensor 1 monitora equipamentos *

Referências

Documentos relacionados

Estudar o efeito da plastificação do ATp com glicerol nas características físico-químicas da blenda PLA/ATp; Analisar a mudança na cristalinidade dos laminados submetidos a

O romance Usina, diferentemente dos demais do conjunto da obra pertencente ao ciclo-da-cana-de-açúcar, talvez em função do contexto histórico em que se insere, não

Podem treinar tropas (fornecidas pelo cliente) ou levá-las para combate. Geralmente, organizam-se de forma ad-hoc, que respondem a solicitações de Estados; 2)

Por isso, o trabalho é uma categoria fundante do ser social, que viabiliza as transformações nas relações materiais de produção e reprodução humana, tendo no

O Plano de Manutenção dos Monumentos é uma ferramenta de gestão e de conservação preventiva que tem os seguintes objetivos: melhorar a gestão de recursos (reduzindo custos a

O termo extrusão do núcleo pulposo aguda e não compressiva (Enpanc) é usado aqui, pois descreve as principais características da doença e ajuda a

Apesar dos esforços para reduzir os níveis de emissão de poluentes ao longo das últimas décadas na região da cidade de Cubatão, as concentrações dos poluentes

Identificar a língua espanhola como instrumento de acesso a informações, a outras culturas e grupos sociais com foco na área de Recursos Humanos.. Aplicar