Resolução do Exame de 1ª época

Introdução à Programação

IGE e ETI

1º semestre de 2001/2002

ISCTE


A azul (e indentadas uma vez) as respostas às questões.
Questão 1
Assinale com V (Verdadeiro) as expressões que estão correctas e com F (Falso) as que estão incorrectas.

Deve preencher todos os espaços indicados por um sublinhado (___) com V ou F. Qualquer espaço não preenchido será considerado como uma resposta errada.

Em geral as alíneas podem ter zero ou mais respostas correctas.  Cada resposta correctamente assinalada vale 0,5 valores.

Nas alíneas em que apenas uma resposta está correcta (se existirem estão assinaladas no texto), responder com mais ou menos do que um V anula a cotação.  A resposta correcta corresponde à cotação completa.  Qualquer outra resposta corresponde a zero valores.

Em todos os casos em que não é explicitamente referida a localização de uma instrução, considere que esta é dada na função main() do programa seguinte (Atenção: o código não tem erros de compilação, mas pode ter erros lógicos):

#include <iostream>
#include <string>
#include <vector>

using namespace std;

class Dicionário {

  public:
    Dicionário(string const& língua);

    string const& língua() const;
    bool contém(string const& palavra) const;
    int númeroDePalavras() const;

    void insere(string const& palavra);

    void transfereDe(Dicionário origem);

    void mostra() const;


  private:

    vector<string> palavras;
    string const língua_;
};

Dicionário::Dicionário(string const& língua)

    : língua_(língua) 
{

}

void Dicionário::insere(string const& palavra)
{
    palavras.push_back(palavra);
}

void Dicionário::transfereDe(Dicionário origem)
 
{
    while(not origem.palavras.empty()) {
        palavras.push_back(origem.palavras.back());
        origem.palavras.pop_back();
    }

}

...

int main()
{
    string const português("Português");
    Dicionário dicionário_de_português(português);
    Dicionário const dicionário_de_expressões_usadas_no_vácuo("Expressão no vácuo");
   
...
}

1.1  Admita que qualquer uma destas instruções é dada na função main() imediatamente após a definição das variáveis.  Quais das seguintes instruções estão correctas?

 V   Dicionário d(dicionário_de_expressões_usadas_no_vácuo.lingua());
 F   Dicionário d;

A classe Dicionário não tem um construtor por omissão.

 F   Dicionário d(italiano);

O construtor da classe Dicionário tem como parâmetro uma referência constante para uma string.

[cotação: 1,5]

1.2  Admita que qualquer uma destas instruções é dada na função main() do programa acima. Quais das seguintes instruções estão correctas?

 V  bool contém_palavra = dicionário_de_português.contém("exame");
 F  dicionário_de_português.lingua() = "Castelhano";

lingua() é uma função membro constante que devolve uma string por referência constante, pelo que não se lhe podem atribuir valores.

 F  português = dicionário_de_expressões_usadas_no_vácuo.lingua();

português é uma string constante, pelo que não se lhe podem atribuir novos valores.

 V  int const n = dicionário_de_expressões_usadas_no_vácuo.númeroDePalavras();

[cotação: 2]

1.3  Assuma que as seguintes instruções são dadas dentro de uma função ou procedimento membro da classe Dicionário e que não são declaradas quaisquer variáveis ou constantes dentro dessa função ou procedimento (que não tem quaisquer parâmetros).  Quais das seguintes instruções estão correctas?

 F   palavras = númeroDePalavras();

palavras é um vector, pelo que não se lhe pode atribuir um valor do tipo int.

 F   língua_ = "Castelhano";

língua_ é um atributo constante, pelo que tem de ser inicializado no construtor.

 [cotação: 1]

1.4  Assumindo que os restantes métodos da classe estão definidos, qual é o resultado da invocação do seguinte programa?

int main()
{
    Dicionário a("Português europeu");
    a.insere("alquimia");
    a.insere("banana");
    a.insere("dia");

    Dicionário b("Português americano");
    b.transfereDe(a);

    cout << a.númeroDePalavras() << " "
         << b.
númeroDePalavras() << endl;
}

Apenas uma das opções está correcta.

 V   3 3
 F   0 3
 F   3 0
 F   0 0

[cotação: 0,5]

O atributo língua_ da variável a do tipo Dicionário é inicializado com "Português europeu" e no atributo vector palavras são inseridas três cadeias de caracteres: "alquimia", "banana" e "dia".  O atributo língua_ da variável b do tipo Dicionário é inicializado com "Português americano" e o atributo palavras é um vector vazio.  Em seguida, a variável b invoca a operação transfereDe(), com a variável a como argumento.  O conteúdo do vector membro de a é inserido no vector membro de b (ficando assim com três elementos), mas como o argumento foi passado por valor, as alterações realizadas dentro do método não se reflectem em a.  Assim, quando se invoca a operação númeroDePalavras() para as variáveis  a e b obtém-se em ambos os casos 3.

Questão 2

Considere o problema de gestão de stocks numa empresa.  No stock da empresa dão entrada lotes, que se caracterizam por ter o nome do produto, a quantidade de unidades do produto nesse lote e o valor unitário do produto nesse lote.  Quando um lote de um produto chega ao armazém da empresa é registado no inventário.  Quando ocorrem saídas de produtos do armazém altera-se o inventário de acordo com a quantidade de produto que saiu do armazém.  A ordem de saída dos produtos respeita a ordem de chegada. Ou seja, começa-se por retirar os produtos do primeiro lote desse produto que tiver dado entrada, continuando-se pelos lotes seguintes (do mesmo produto) até se ter a quantidade pretendida.  O valor de uma saída é calculado somando as multiplicações das quantidade saídas de cada lote pelo valor unitário do produto nesse lote.  O valor dos produtos em stock é calculado somando as multiplicações das quantidades de cada lote inventariado pelo valor unitário do produto nesse lote.

Pretende-se construir um sistema de gestão de stocks de uma empresa que disponha das seguintes funcionalidades:

2.1  Defina a classe C++ Lote que representa um lote de um dado produto. Um lote, como já foi referido, é constituído pelo nome (cadeia de caracteres), quantidade (inteiro) e valor unitário (valor de vírgula flutuante, i.e., double) do produto.  A classe C++ Lote deve dispor de um construtor que recebe os dados necessários à construção de um lote e das seguinte operações:

  1. Um inspector para o nome do produto;
  2. um inspector para a quantidade;
  3. um inspector para o valor unitário;
  4. um modificador que permite diminuir a quantidade de produto de um lote, dada a quantidade que se pretende retirar do lote; e
  5. uma operação que exiba no ecrã os dados do lote.

Indique a condição invariante de classe (CIC) e as pré condições de cada operação. Adicione a operação necessária para verificar a CIC.

Indique claramente quais dos métodos da classe não alteram a instância implícita.

Não é necessário nesta questão definir qualquer um dos métodos (funções ou procedimentos membro) da classe.

[cotação: 1]

2.2  Defina o construtor da classe Lote e o método que permite verificar a CIC.

[cotação: 0,5]

2.3  Defina as funções de inspecção do nome, da quantidade e valor unitário do produto para a classe Lote, bem como a operação que exibe os dados do lote no ecrã e o modificador que permite diminuir a quantidade de produto do lote.

Coloque instruções de asserção para verificar a condição invariante de classe e as pré-condições nos métodos definidos.

[cotação: 0,5]

2.4  Defina a classe C++ Inventário que permite registar os lotes que chegam ao armazém da empresa e as saídas de produto.  Esta classe deve dispor de operações para:

  1. Saber se existe um dado produto no inventário;
  2. Devolver a quantidade de dado produto;
  3. Devolver o valor de uma saída de produto do armazém, dado o nome do produto e a quantidade;
  4. Devolver o valor dos produtos em stock;
  5. Adicionar um novo lote ao inventário;
  6. Remover do inventário uma dada quantidade de um dado produto correspondente a uma saída de produto do armazém;
  7. Mostrar o inventário.

Todos os lotes inventariados têm de ter quantidade superior a zero.  Como não devem existir no inventário lotes com quantidade igual a zero (não fazem sentido), a classe C++ Inventário deve dispor ainda de uma operação privada para realizar a purga dos lotes vazios, isto é, remover todos os lotes cuja quantidade é zero.

Indique qual a condição invariante de classe e as pré-condições das operações.  Adicione a operação necessária para verificar a CIC.

Indique claramente quais dos métodos da classe não alteram a instância implícita.

Não é necessário nesta questão definir qualquer um dos métodos.

[cotação: 1,5]

2.5  Defina o método da classe C++ Inventário que permite saber se dado produto está presente no inventário.

Coloque instruções de asserção para verificar as pré-condições e a condição invariante de classe no método definido.

[cotação: 1]

2.6  Defina o método da classe C++ Inventário que devolve o valor dos produtos em stock.  

Coloque instruções de asserção para verificar as pré-condições e a condição invariante de classe no método definido.

[cotação: 1]

2.7  Defina os métodos da classe C++ Inventário para:

Coloque instruções de asserção para verificar as pré-condições e a condição invariante de classe nos métodos definidos.

[cotação: 1,5]

2.8  Escreva um programa que utilize as classes anteriores para simular a componente de gestão de stocks de uma empresa.  O programa deve mostrar o menu:

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

no ecrã, permitindo assim ao utilizador escolher entre as operações possíveis.  Quando a opção 0 for escolhida o programa terminará.

Se a opção 1 for escolhida, o programa deve perguntar qual o nome do produto, a quantidade e o valor unitário, e adicionar ao inventário o lote constituído por essa informação.

Se a opção 2 for escolhida, o programa deve perguntar qual o nome do produto e a quantidade que irá sair do armazém. Se o produto não existir em stock o programa deve avisar o utlizador. Se o produto não existir em quantidade suficiente o programa deve avisar o utilizador. Se não ocorrer nenhuma destas situações, o programa deve mostrar o valor da saída e realizá-la.

Se for escolhida a opção 3, deve ser exibido no ecrã o valor dos produtos em stock.  Caso seja escolhida a opção 4, exibe-se o inventário.

Exemplo de execução:

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 1

Nome do produto: batata
Quantidade: 100
Valor unitário: 0.7

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 4

batata    100    0.7

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 1

Nome do produto: cebola
Quantidade: 50
Valor unitário: 0.85

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 1

Nome do produto: batata
Quantidade: 100
Valor unitário: 0.5

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 4

batata    100    0.7
cebola     50    0.85
batata    100    0.5

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 2

Nome do produto: banana
Quantidade: 20

O produto banana não existe em stock.

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 2

Nome do produto: batata
Quantidade: 150

O valor da saída é 95.

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 4

cebola    50    0.85
batata    50    0.5

1 - Entrada de lote
2 - Saída de produto
3 - Valor dos produtos em stock

4 - Mostra inventário

0 - Terminar

Opção: 3

O valor dos produtos em stock é 67.5.

[cotação: 1]

Apresenta-se a solução completa.  A negrito as partes requeridas no enunciado.

#include <iostream>
#include <iomanip>
#include <string>
#include <vector>

using namespace std;

/**
Representa um lote.
    @invariant nome_ <> "" e quantidade_ >= 0 e valor_unitario >= 0. */
class Lote {
  public:
    /**
Constrói um lote.
        @pre nome <> "", quantidade >= 0 e valor_unitario >= 0.
       
@post nome() = nome, quantidade() = quantidade
              valor_unitario() = valor_unitario
. */
    Lote(string const& nome, int const quantidade, 
         double const valor_unitario);

    /**
Devolve o nome do produto do lote.
        @pre V.
       
@post nome() é o nome do produto do lote. */
    string const& nome() const;

    /**
Devolve a quantidade de produto do lote.
        @pre V.
        @post quantidade() é a quantidade de produto do lote. */
    int quantidade() const;

    /**
Devolve o valor unitário do produto do lote.
        @pre V.
        @post valorUnitario() é o valor unitário do produto do lote. */
    double valorUnitario() const;

    /**
Diminui a quantidade de produto do lote em quantidade.
        @pre quantidade <= quantidade() e quantidade() = q.
       
@post quantidade() = q - quantidade. */
    void retira(int const quantidade);

    /**
Mostra informação sobre o lote no ecrã.
        @pre V.
        @post ¬cout ou o ecrã contém o nome, a quantidade e o valor unitário do lote. */
    void mostra() const;

  private:
    string nome_;
    int quantidade_;
    double valor_unitario;

    /**
Devolve verdadeiro se o invariante se verificar.
        @pre V.
        @post cumpreInvariante = (nome_ <> "" e quantidade_ >= 0 e 
              valor_unitario >=
0).
*/
    bool cumpreInvariante() const; 
};

Lote::Lote(string const& nome, int const quantidade, 
           double const valor_unitario)
    : nome_(nome), quantidade_(quantidade),
      valor_unitario(valor_unitario) {
}

string const& Lote::nome() const
{
    assert(cumpreInvariante());

    return nome_;
}

int Lote::quantidade() const
{
    assert(cumpreInvariante());

    return quantidade_;
}

double Lote::valorUnitario() const
{
    assert(cumpreInvariante());

    return valor_unitario;
}

void Lote::mostra() const
{
    assert(cumpreInvariante());

    cout << setw(40) << nome_ << ' '
         << setw(10) << quantidade_ << ' '
         << setw(20) << valor_unitario << endl;
}

void Lote::retira(int const quantidade)
{
    assert(cumpreInvariante());
    assert(quantidade <= quantidade_);

    quantidade_ -= quantidade;

    assert(cumpreInvariante());
}

bool Lote::cumpreInvariante() const
{
    return nome_ != "" and 0 <= quantidade_ and 0 <= valor_unitario;
}

/**
Representa um inventário onde são registados lotes.
    @invariant (Q j : 0 <= j < lotes.size() : 0 < lotes[j].quantidade()). */
class Inventario {
  public:
    /**
Indica se está inventariado o produto com nome nome_do_produto.
        @pre V.
        @post contemProdutoComNome = *this contém pelo menos uma unidade do
               produto com nome nome_do_produto. */
    bool contemProdutoComNome(string const& nome_do_produto) const;

    /**
Devolve a quantidade inventariada do produto com nome nome_de_produto.
       
@pre V.
        @post quantidadeDe = quantidade total de produto nos lotes cujo nome
              de produto é nome_do_produto. */
    int quantidadeDe(string const& nome_do_produto) const;

    /**
Devolve o valor da saída do armazém de quantidade do produto com nome nome_do_produto.
       
@pre 0 <= quantidade e quantidade <= quantidadeDe(nome_do_produto).
       
@post custoDaSaidaDe = valor total da próxima saída de quantidade produto
              de nome nome_do_produto. */
    double custoDeSaidaDe(string const& nome_do_produto, int const quantidade) const;

    /**
Devolve o valor dos produtos inventariados.
        @pre V.
       
@post valor = valor total dos produtos inventariados. */
    double valor() const;

    /**
Adiciona o lote recebido como argumento no fim do inventario.
        @pre quantidadeDe(lote.nome()) = q e custoDeSaidaDe(lote.nome(), q) = c.
       
@post contemProdutoComNome(lote.nome())
             quantidadeDe(lote.nome()) = q
+ lote.quantidade() e
             custoDeSaidaDe(lote.nome(),
q + lote.quantidade()) =
                
c + lote.quantidade() × lote.valorUnitario(). */ 
    void adiciona(Lote const& lote);

    /**
Remove do inventário a quantidade quantidade do produto nome_do_produto.
       
@pre contemProdutoComNome(nome_do_produto)
             0 <= quantidade e quantidade <= quantidadeDe(nome_do_produto) e
             quantidadeDe(nome_do_produto) =
q e
             custoDeSaidaDe(nome_do_produto,
q) = c1 e
             custoDeSaidaDe(nome_do_produto, quantidade) =
c2.
       
@post quantidadeDe(nome_do_produto) = q - quantidade e
              custoDeSaidaDe(nome_do_produto, quantidadeDe(nome_do_produto)) = c1 - c2. */
    void remove(string const& nome_do_produto, int const quantidade);

    /**
Mostra no ecrã informação sobre todos os lotes.
        @pre V.
        @post ¬canal ou o ecrã contém o nome, a quantidade e o valor unitário de todos os lotes. */
    void mostra() const;

  private:
    vector<Lote> lotes;

    /**
Remove do vector os lotes com quantidade igual a 0.
        @pre V.
        @post (Q j : 0 <= j < lotes.size() : lotes[j].quantidade() <> 0). */
    void purgaLotesVazios();

    /**
Devolve verdadeiro se o invariante se verificar.
        @pre V.
       
@post cumpreInvariante =
              
(Q j : 0 <= j < lotes.size() : lotes[j].quantidade() > 0).
*/
    bool cumpreInvariante() const; 
};

bool Inventario::contemProdutoComNome(string const& nome_do_produto) const
{
    assert(cumpreInvariante());

    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        if(lotes[i].nome() == nome_do_produto)
            return true;
    return false;
}

int Inventario::quantidadeDe(string const& nome_do_produto) const
{
    assert(cumpreInvariante());

    int quantidade = 0;

    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        if(lotes[i].nome() == nome_do_produto)
            quantidade += lotes[i].quantidade();

    return quantidade;
}

double Inventario::custoDeSaidaDe(string const& nome_do_produto, 
                                  int const quantidade) const
{
    assert(cumpreInvariante());
    assert(0 <= quantidade and quantidade <= quantidadeDe(nome_do_produto));

    double custo = 0.0;
    int quantidade_actual = 0;
    for(vector<Lote>::size_type i = 0;
        i != lotes.size() and quantidade_actual != quantidade;
        ++i)
        if(lotes[i].nome() == nome_do_produto) {
            int unidades_a_acrescentar = 
                lotes[i].quantidade() < quantidade - quantidade_actual ?
                    lotes[i].quantidade() : quantidade - quantidade_actual;
            quantidade_actual = unidades_a_acrescentar;
            custo += unidades_a_acrescentar*lotes[i].valorUnitario();
        }

    return custo;
}

double Inventario::valor() const
{
    assert(cumpreInvariante());

    double total = 0.0;
    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        total += lotes[i].quantidade() * lotes[i].valorUnitario();
    return total;
}

void Inventario::adiciona(Lote const& lote) 
{
    assert(cumpreInvariante());

    lotes.push_back(lote);

    assert(cumpreInvariante());
}

void Inventario::remove(string const& nome_do_produto, int const quantidade)
{
    assert(cumpreInvariante());
    assert(0 <= quantidade and quantidade <= quantidadeDe(nome_do_produto));

    int quantidade_por_remover = quantidade;
    while(quantidade_por_remover != 0)
        if(lotes[i].nome() == nome_do_produto) {
            int quantidade_a_remover = 
                min(lotes[i].quantidade(), quantidade_por_remover);
            quantidade_por_remover -= quantidade_a_remover;
            lotes[i].retira(quantidade_a_remover);
        }

    purgaLotesVazios();

    assert(cumpreInvariante());
}

void Inventario::mostra() const
{
    assert(cumpreInvariante());

    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        lotes[i].mostra();
}

void Inventario::purgaLotesVazios()
{
    int j = 0;
    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        if(lotes[i].quantidade() != 0)
            lotes[j++] = lotes[i];

    lotes.erase(lotes.begin() + j, lotes.end());
}

bool Inventario::cumpreInvariante() const
{
    for(vector<Lote>::size_type i = 0; i != lotes.size(); ++i)
        if(lotes[i].quantidade() <= 0)
            return false;
    return true;
}

int main()
{
    Inventario inventario;

    while(true) {
        cout << endl
             << "1. Entrada de um lote" << endl
             << "2. Saída de produto" << endl
             << "3. Mostra valor dos produtos em stock" << endl
             << "4. Mostra inventário" << endl
             << endl
             << "0. Terminar" << endl
             << endl
             << "Opção: ";
        int opcao;
        cin >> opcao;

        switch(opcao) {
          case 0:
            cout << "Até breve." << endl;
            return 0;

          case 1: {
              cout << "Nome do produto: ";
              string nome;
              cin >> nome;

              cout << "Quantidade: ";
              int quantidade;
              cin >> quantidade;

              cout << "Preço unitário: ";
              double preco;
              cin >> preco;

              inventario.adiciona(Lote(nome, quantidade, preco));

              break;
          }

          case 2: {
              cout << "Nome do produto: ";
              string nome;
              cin >> nome;

              if(inventario.contemProdutoComNome(nome)) {
                  cout << "Quantidade: ";
                  int quantidade;
                  cin >> quantidade;

                  if(inventario.quantidadeDe(nome) >= quantidade) {
                      cout << "O custo da saída é "
                           << inventario.custoDeSaidaDe(nome, quantidade) << endl;
                      inventario.remove(nome, quantidade);
                  } else
                      cout << "Não existem " << quantidade
                           << " unidades disponíveis de " << nome << endl;
              } else
                  cout << "Não exite o produto " << nome << " em stock." << endl;

              break;
          }

          case 3:
            cout << endl
                 << "O valor dos produtos em stock é "
                 << inventario.valor() << '.' 
                 << endl;
            break;

          case 4:
            inventario.mostra();
            break;

          default:
            cout << "Opção inválida!" << endl;
        }
    }
}

Questão 3

Com a entrada do euro em circulação, a necessidade de realizar conversões de valores entre unidades monetárias generalizou-se.  O nosso objectivo é simplificar o trabalho ao grande número de pessoas que ainda necessitam de realizar conversões para melhor compreender o real valor dos bens e serviços.

Defina uma classe Conversor que permita converter valores, dada a taxa de conversão.

Deve definir a classe e os seguintes métodos:

  1. Conversor::Conversor(double const taxa_de_conversão, int const precisão = 2) que constrói um conversor com taxa de conversão taxa_de_conversão e precisão precisão.
  2. vector<double> Conversor::conversãoDe(vector<double> const& valores) const que devolve a conversão do vector valores.
  3. void Conversor::mostraTabela(double const valor_inicial, double const valor_final, double const incremento) const que exibe no ecrã uma tabela de conversão de valor_inicial a valor_final (inclusive) com incrementos de incremento.
  4. Uma função de inspecção para a taxa de conversão.
  5. Uma função de inspecção para a precisão.
  6. void Conversor::mudaPrecisãoPara(int const precisão) que permite alterar o valor da precisão.

e a função

  1. double arredondamentoDe(double const valor, int const precisão), sabendo que double Conversor::conversãoDe(double const valor) const definido como

    double Conversor::conversãoDe(double const valor) const
    {
        return arredondamentoDe(valor * taxa_de_conversão, precisão_);
    }

    devolve a conversão do valor valor.

de modo a que o seguinte código seja válido:

Conversor conversor(200.482);
cout << conversor.conversaoDe(1) << endl; //
Aparece 200.48
                                          // Note que .48 aparece porque a precisão é 2
conversor.estabelecePrecisão(0);
cout << conversor.conversaoDe(2) << endl; //
Aparece 401
                                          // Note que não aparece parte decimal porque a precisão é 0

Caso seja necessário pode usar a função double pow(double base, int expoente) para calcular potências.

[cotação: 3,5]

#include <iostream>
#include <iomanip>
#include <cmath>
#include <vector>

using namespace std;

/**
Devolve o valor valor recebido como argumento, arredondado com precisão precisao.
    @pre 0 <= precisao.
   
@post arredondamentoDe = arredondamento de valor com precisão precisao. */
double arredondamentoDe(double const valor, int const precisao)
{
    if(valor < 0)
        return ceil(valor * pow(10, precisao) - 0.5) / pow(10, precisao);
    else
        return floor(valor * pow(10, precisao) + 0.5) / pow(10, precisao);
}

/**
Representa conversores entre unidades de medida.
    @invariant taxa_de_conversao <> 0 e 0 <= precisao_. */
class Conversor {
  public:
    /**
Constrói um conversor.
        @pre taxa_de_conversao <> 0 e 0 <= precisao.
       
@post taxaDeConversao() = taxa_de_conversao e precisao() = precisao. */
    Conversor(double const taxa_de_conversao, int const precisao = 2);

    /**
Devolve a taxa de conversão.
        @pre V.
       
@post taxaDeConversao() é a taxa de conversão do conversor. */
    double taxaDeConversao() const;

    /**
Devolve a precisão do conversor.
        @pre V.
        @post precisao()
é a precisão do conversor. */
    int precisao() const;

    /**
Devolve a conversão do valor recebido como argumento.
        @pre V.
        @post conversaoDe = arredondamentoDe(valor × taxaDeConversao(), precisao()). */
    double conversaoDe(double const valor) const;

    /**
Devolve a conversão de todos os valores do vector recebido como argumento.
        @pre V.
        @post conversaoDe.size() = valores.size() e 
             
(Q j : 0 <= j < valores.size() : conversaoDe[j] =
               arredondamentoDe(valores[j] ×
taxaDeConversao(), precisao())) */
    vector<double> conversaoDe(vector<double> const& valores) const;

    /**
Estabelece a precisão do arredondamento na conversão.
        @pre 0 <= precisao.
       
@post precisao() = precisao. */
    void mudaPrecisaoPara(int const precisao);

    /**
Mostra no ecrã uma tabela de conversão.
       
@pre (inicio <= fim e 0 < incremento) ou (fim <= inicio e incremento < 0) ou
             (inicio = fim e incremento = 0).
       
@post ¬canal
ou o ecrã contém uma tabela de conversão com início em inicio, fim em fim e
                            com incrementos de incremento. */
    void mostraTabela(double const inicio, double const fim, double const incremento) const;

  private:
    double const taxa_de_conversao;
    int precisao_;

    /**
Devolve verdadeiro se o invariante se verificar.
       
@pre V.
       
@post cumpreInvariante = (taxa_de_conversao <> 0 e precisao_ >= 0). */
    bool cumpreInvariante() const;
};

Conversor::Conversor(double const taxa_de_conversao, int const precisao)
    : taxa_de_conversao(taxa_de_conversao), precisao_(precisao)
{
    assert(cumpreInvariante());
}

double Conversor::taxaDeConversao() const
{
    assert(cumpreInvariante());

    return taxa_de_conversao;
}

int Conversor::precisao() const
{
    assert(cumpreInvariante());

    return precisao_;
}

double Conversor::conversaoDe(double const valor) const
{
    assert(cumpreInvariante());

    return arredondamentoDe(valor * taxa_de_conversao, precisao_);
}

vector<double> Conversor::conversaoDe(vector<double> const& valores) const
{
    assert(cumpreInvariante());

    vector<double> valores_convertidos;
    for(vector<double>::size_type i = 0; i != valores.size(); ++i)
        valores_convertidos.push_back(conversaoDe(valores[i]));

    return valores_convertidos;
}

void Conversor::mudaPrecisaoPara(int const precisao)
{
    assert(cumpreInvariante());
    assert(0 <= precisao);

    precisao_ = precisao;

    assert(cumpreInvariante());
}

void Conversor::mostraTabela(double const inicio, double const fim,
                             double const incremento) const
{
    assert(cumpreInvariante());
    assert(inicio <= fim and 0 < incremento or
           fim <= inicio and incremento < 0 or
           inicio == fim and incremento == 0);

    if(incremento < 0)
        for(double valor = inicio; fim <= valor; valor += incremento)
            cout << setw(10) << valor << " <-> " 
                 << setw(10) << conversaoDe(valor) << endl;
    else
        for(double valor = inicio; valor <= fim; valor += incremento)
            cout << setw(10) << valor << " <-> "
                 << setw(10) << conversaoDe(valor) << endl;
}

bool Conversor::cumpreInvariante() const
{
    return taxa_de_conversao != 0 and 0 <= precisao_;
}

Questão 4

Considere a função int índiceDoMáximoDe(vector<int> const& v) que devolve o índice do valor máximo que ocorre num vector:

int índiceDoPrimeiroMáximoDe(vector<int> const& v)
{
    int índice_do_máximo = 0;

    for(vector<int>::size_type i = 1; i != v.size(); ++i)
        if(v[índice_do_máximo] < v[i])
            índice_do_máximo = i;

    return índice_do_máximo;
}

4.1  Indique a pré-condição (PC), a condição objectivo (CO), a condição invariante (CI) e a guarda (G) do ciclo necessário à construção desta função.

[cotação: 1]

PC: 1 <= v.size().
CO: 0 <= índiceDoPrimeiroMáximoDe < v.size() e 
        (Q j : 0 <= j < v.size() : v[j] <= v[índiceDoPrimeiroMáximoDe]) e
       
(Q j : 0 <= j < índiceDoPrimeiroMáximoDe : v[j] < v[índiceDoPrimeiroMáximoDe]).
CI: 0 <= índice_do_máximo < i
        (Q j : 0 <= j < i : v[j] <= v[índice_do_máximo]) e
       
(Q j : 0 <= j < índice_do_máximo : v[j] < v[índice_do_máximo]) e
   
     0 <= i <= v.size().
G: i <> v.size().

4.2  Prove que o ciclo está correcto verificando que:

  1. Se a PC for verdadeira, então a CI também é verdadeira após a inicialização das variáveis e antes da primeira iteração do ciclo, i.e.:
      // PC
      init
      // implica CI.
  2. Se a guarda G e a CI  do ciclo forem verdadeiras no início de um passo, então a CI também é verdadeira no fim desse passo, i.e.:
      // G e CI
      passo
      // implica CI
  3. Se a guarda G for falsa e a CI for verdadeira, então a CO é verdadeira, i.e.:
      // CI e ¬G implica CO.
Ver Secção 5.3.3 do Capítulo 5 as Folhas Teóricas.

[cotação: 1,5]

Questão 5

Explique resumida mas claramente os conceitos de abstracção e encapsulamento.

[cotação: 1]

R: Ver Secção 3.1 (Introdução à modularização) do Capítulo 3 das Folhas Teóricas.