Resolução da Frequência

Introdução à Programação

IGE e ETI

1º semestre de 1999/2000

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:

#include <iostream>
#include <vector>

using namespace std;

class A {
  public:
    int z;
    A(const int a = 0, const int b = 1);
    int f();

  private:
    int x;
};

...

int main()
{
    int i = 1, j = 2;
    A meu_a(10);
    const A teu_a(10);
    vector<int> v(2);
    v[0] = v[1] = 10;
    ...
}

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

 V  A a(v[0], v[1]);
 V  A a;
 F  A a(v[2]);

O vector v só tem dois elementos, de índices 0 e 1.  Não existe elemento de índice 2!
 [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  meu_a.z = 1;

A variável membro z é pública.
 F  meu_a.x = 1;
A variável membro x é privada.
 F  teu_a.z = 1;
A variável membro z é pública, mas é membro de uma constante (teu_a), pelo que o seu valor não pode ser alterado!
 F  int z = teu_a.f();
A função membro f() não foi declarada como const, pelo que o compilador assume que altera a instância implícita, que neste caso é uma constante.  Assim, o compilador proíbe a invocação de f().
 [cotação: 2]

1.3  Assuma que as seguintes instruções são dadas dentro de uma função ou procedimento membro da classe A 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  a.x = 1;

Não exite qualquer variável a visível!
 V  x = 1;

 [cotação: 1]

1.4  Suponha a seguinte função:

int umaFuncao(int x)
{
    if(x < 0)
        return 0;
    for(int i = 0; i != x; ++i) {
        int z;
        z += x;
    }
    return z;
}
Assumindo que não existem quaisquer variáveis globais no programa em que a função está inserida

 F o código da função está correcto?

A variável z só é visível até ao fecho do bloco em que foi definida, pelo que não é visível na instrução de retorno.
 [cotação: 0,5]

Questão 2

2.1  Dada a declaração da estrutura Aluno:

struct Aluno {
    Aluno(const string& nome, int nota)
        : nome(nome), nota(nota) {
    }
    string nome;
    int nota;
};
Defina uma classe Pauta de modo a que possa conter informação sobre 100 alunos.  A classe Pauta deve conter funções e procedimentos para: Notas:
  1. Caso lhe pareça necessário declare também um construtor apropriado para a classe Pauta.
  2. Nenhuma das funções ou procedimentos atrás indicados, com excepção do último, escreve quaisquer dados no ecrã.
  3. Todas as funções ou procedimentos membro que não alteram a instância implícita devem indicá-lo explicitamente.
  4. Todos os parâmetros que não são alterados dentro da função ou procedimento que os usa devem ser indicados explicitamente.
  5. A definição da classe Pauta deve conter a declaração das funções e procedimentos membro e a definição das variáveis membro.
  6. Não é necessário nesta alínea definir quaisquer funções ou procedimentos membro da classe.
[cotação: 3,5]
// Solução usando vectores (sem necessidade de limite no número de alunos):
class Pauta {
  public:
    static const int número_máximo_de_alunos = 100;

    // Não é necessário construtor: o C++ fornece um construtor automaticamente que invoca o
    // construtor por omissão do vector de alunos (que constrói um vector vazio, como pretendido).
    void insere(const Aluno& aluno);
    void remove(const string& nome);
    bool existe(const string& nome) const;
    Aluno aluno(const string& nome) const;
    bool cheia() const;
    bool vazia() const;
    void mostra() const;

  private:
    vector<Aluno> alunos;
}

// Solução usando matrizes clássicas do C++ (obriga a alterar a estrutura Aluno de modo a ter
// construtor por omissão):

struct Aluno {
    Aluno(const string& nome = "", int nota = 20)
        : nome(nome), nota(nota) {
    }
    string nome;
    int nota;
};

class Pauta {
  public:
    static const int número_máximo_de_alunos = 100;

    // Neste caso é necessário construtor para inicializar o contador de alunos na
    // pauta (número_de_alunos):
    Pauta();
    void insere(const Aluno& aluno);
    void remove(const string& nome);
    bool existe(const string& nome) const;
    Aluno aluno(const string& nome) const;
    bool cheia() const;
    bool vazia() const;
    void mostra() const;

  private:
     Aluno alunos[número_máximo_de_alunos];
     int número_de_alunos;
}

2.2  Escreva um programa que utilize a classe Pauta (descrita e definida na alínea anterior).  Este programa deve pedir ao utilizador (e ler do teclado) informação sobre 10 alunos.  Deve guardar essa informação e, no final, mostrar a pauta completa.  Deve impedir a inserção de nomes repetidos: em caso de repetição deve avisar o utilizador e manter a pauta tal como estava (ou seja, se os 10 nomes introduzidos pelo utilizador forem todos iguais, a pauta conterá apenas aluno com a primeira das notas introduzidas).

Notas:

  1. Não é necessário repetir a declaração da classe Pauta ou definir qualquer um dos seus métodos nesta alínea.
  2. Considere que toda a classe Pauta já se encontra correctamente definida no mesmo ficheiro e que já estão incluídos os ficheiros de cabeçalho das bibliotecas necessárias ao funcionamento do programa (os #include).
  3. Assuma que todos os nomes de alunos são compostos apenas por uma palavra.
[cotação: 3]
int main()
{
    Pauta pauta;

    for(int i = 0; i != 10; ++i) {
        cout << "Introduza nome e nota: ";
        string nome;
        int nota;
        cin >> nome >> nota;
        if(pauta.existe(nome))
            cout << "Esse nome já existe!  Ignorado!" << endl;
        else
            pauta.insere(Aluno(nome, nota));
    }
    pauta.mostra();
}

Questão 3

Suponha uma classe ConjuntoInt para representar conjuntos de inteiros

class ConjuntoInt {
  public:
    void insere(const int n);       // insere o inteiro n no conjunto (se já pertencer ao
                                    // conjunto não faz nada).
    void remove(const int n);       // remove o inteiro n do conjunto (se não pertencer
                                    // ao conjunto não faz nada).
    bool existe(const int n) const; // devolve true se o inteiro n pertencer ao conjunto.

  private:
    vector<int> elementos;          // guarda os elementos do conjunto, sendo o seu
                                    // tamanho (elementos.size()) o número de
                                    // elementos no conjunto, ou seja, o seu cardinal.
};

inline void ConjuntoInt::insere(const int n) {
    if(!existe(n))
        elementos.push_back(n);
}

bool ConjuntoInt::existe(const int n) const
{
    // vector<int>::size_type é um tipo inteiro (provavelmente unsigned int).
    for(vector<int>::size_type i = 0; i != elementos.size(); ++i)
        if(elementos[i] == n)
            return true;
    return false;
}

inline void ConjuntoInt::remove(const int n) {
    ...
}

A classe pode-se usar como se segue:
ConjuntoInt c; // c = {}

c.insere(2);   // c = {2}
c.insere(3);   // c = {2, 3}
c.insere(2);   // c = {2, 3}
c.remove(2);   // c = {3}
if(!c.existe(2))
    cout << "O 2 não está no conjunto!" << endl;

que escreve no ecrã:
O 2 não está no conjunto!
Defina o método void ConjuntoInt::remove(const int) da classe.  Lembre-se que, se a remoção tiver sucesso, o cardinal do conjunto diminui de 1!

 [cotação: 3,5]

inline void ConjuntoInt::remove(const int n) {
    for(vector<int>::size_type i = 0; i != elementos.size(); ++i)
        if(elementos[i] == n) {
            elementos[i] = elementos[elementos.size() - 1];
            elementos.pop_back();
            // ou elementos.resize(elementos.size() - 1);
            return;
        }
}
Questão 4

Suponha a função double soma(const double m[], const int n) que devolve a soma dos valores existentes numa matriz m de double com n elementos:

double soma(const double m[], const int n)
{
    double soma = 0.0;
    int i = 0;
    while(i != n) {
        soma += m[i];
        ++i;
    }
    return soma;
}
4.1  Indique a pré-condição (PC), a condição objectivo (CO) e a condição invariante (CI) do ciclo interno da função.

[cotação: 1,5]

PC: n >= 0
CO: soma = (Sj : 0 <= j < n : m[j])
CIsoma = (S j : 0 <= j < i : m[j]) e 0 <= i <= n
4.2  Prove que o ciclo está correcto verificando que: [cotação: 1,5]

Questão 5

Explique brevemente o que é a abordagem descendente à resolução de problemas e quais as suas vantagens.

[cotação: 2]

Esta abordagem consiste dividir o problema a resolver em sub-problemas mais simples, que por sua vez são abordados da mesma forma, dividindo-os em sub-sub-problemas mais simples, e assim sucessivamente até que os problemas a resolver são triviais e de tradução directa para a linguagem de programação em uso (e.g., C++).  A cada resolução de um problema faz-se corresponder uma função ou procedimento, pelo que a solução do problema global tem a forma de uma função ou procedimento que invoca as funções ou procedimentos que resolvem os seus sub-problemas, e assim sucessivamente, numa hierarquia de funções e procedimentos, cada um com um objectivo bem definido.

As vantagens principais desta abordagem são: