Introdução à Programação
ETI
1º semestre de 1999/2000
ISCTE
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>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?
#include <string>
#include <vector>using namespace std;
class A {
public:
A(const char c = ' ', const int m = 1, const int n = 0);
string f1(const char);
int f2(int);
char f3();
unsigned t;
private:
string s;
string f1(const unsigned);
};...
int main()
{
const char c = '0';
A am('a', 3);
vector<char> u(2);
vector<int> v(2);
u[0] = 'X';
u[1] = 'M';
v[0] = 7;
v[1] = 13;
...
}
V
A a(u[0], v[0], v[1]);
V
A a(c);
Os últimos dois parâmetros do construtor de A têm valores por omissão (dos argumentos).F A a(u[0], u, v[0]);
O segundo parâmetro do construtor de A é um int e não um vector<char>.[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?
F c = am.f3();
c é uma constante, pelo que o seu valor não pode ser alterado.F string s = am.f1(3U);
f1() é uma função membro privada da classe A, pelo que só pode ser invocada por funções ou procedimentos membro da classe A.V int k = am.f2(c - '0');
f2() é uma função membro pública da classe A.V am.t = v[0] + v[1];
t é uma variável membro pública da classe A do tipo unsigned int. É permitido, embora pouco recomentdável, atribuir um inteiro com sinal a um inteiro sem sinal.[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 am.t = f2(s.size());
Não existe qualquer variável am visível!V s = f1(t);
[cotação: 1]
1.4 Suponha o seguinte:
A::A(const char c, const int m, const int n)Assumindo que os restantes métodos da classe estão definidos, qual é o resultado da invocação do programa?
: t(n) {
for(int i = 0; i != m; ++i)
s += c;
}
char A::f3() {
if(t == s.size())
return s[0];
return char('a' + t);
}
int main() {
A a('x'), b('b', 3, 2);
cout << a.f3() << " " << b.f3() << endl;
}
F
x b
V
a c
F
a b
F
x a
Os dois últimos parâmetros do construtor da A têm valores por omissão de 1 e 0, respectivamente. Assim, a construção da variável a leva a que t seja inicializada com o valor 0 e s fique com exactamente um 'x'. A execução de f3() para a devolve char('a' + 0), ou seja o código do caractere 'a'. A inicialização de b leva à inicialização de t com 2 e à colocação de três 'b' em s. A execução de f3() para b devolve char('a' + 2), ou seja o código do caractere 'c'.[cotação: 0,5]
Questão 2
Considere um projecto de trabalho numa empresa. Um projecto de trabalho é constituído por várias tarefas que têm um dado ínicio e uma dada duração (ambos em dias). Admita que o projecto se inicia no dia 1 e os restantes dias são contandos a partir daqui. Em determinadas situações, como é o caso de certas reestruturações, é necessário adicionar ou remover tarefas de um projecto. Naturalmente, só é possível remover tarefas que ainda não tenham começado, assim como só é possível adicionar tarefas com início depois do dia em que o projecto se encontra.
2.1 Defina a classe Tarefa de modo a guardar:
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 Tarefa.
[cotação: 0,5]
2.3 Defina as funções de inspecção da identificação, início e duração para a classe Tarefa.
[cotação: 0,5]
2.4 Defina a classe Projecto que deve guardar a informação sobre as tarefas que o projecto compreende, bem como o dia actual do projecto. Deve declarar os métodos necessários para:
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. Coloque comentários indicando claramente para que serve cada uma das variáveis membro que definir.
[cotação: 2]
2.5 Defina o método da classe Projecto que remove uma tarefa do projecto dada a sua identificação.
[cotação: 2]
2.6 Defina o método da classe Projecto que adiciona uma nova tarefa ao projecto.
[cotação: 1]
2.7 Escreva um programa que utilize a classe Projecto (descrita e definida anteriormente). Este programa deve inicialmente construir um projecto com n tarefas, n dado pelo utilizador do programa. Para cada tarefa deve ser perguntado ao utilizador qual a sua identificação, o seu início e a sua duração. Em seguida, o programa deverá simular o funcionamento do projecto até este terminar, isto é, até que todas as tarefas estejam terminadas. A simulação terá início com o avanço do dia actual do projecto para o dia 1 e prosseguirá avançando dia a dia. Em cada dia do projecto deverá ser exibido ao utilizador do programa o seguinte menu:
[cotação: 1]
#include <iostream>Questão 3
#include <string>
#include <vector>using namespace std;
class Tarefa {
public:
Tarefa(string const& = "", int const = 0, int const = 0);
string identificacao() const;
int inicio() const;
int duracao() const;private:
string identificacao_;
int inicio_, duracao_;
};inline Tarefa::Tarefa(string const& identificacao, int const inicio,
int const duracao)
: identificacao_(identificacao), inicio_(inicio), duracao_(duracao) {
}inline string Tarefa::identificacao() const {
return identificacao_;
}inline int Tarefa::inicio() const {
return inicio_;
}inline int Tarefa::duracao() const {
return duracao_;
}class Projecto {
public:
typedef vector<Tarefa>::size_type Tamanho;Projecto();
void adiciona(Tarefa const& tarefa);
void remove(string const& identificacao);
void avancaDia();
void mostra() const;
bool concluido() const;private:
vector<Tarefa> tarefas;
int dia_actual;
};inline Projecto::Projecto()
: dia_actual(0) {
}void Projecto::remove(string const& identificacao)
{
for(Tamanho i = 0; i != tarefas.size(); ++i)
if(tarefas[i].identificacao() == identificacao) {
if(tarefas[i].inicio() <= dia_actual)
cerr << "A tarefa não pode ser eliminada..." << endl;
// É esta a melhor forma de resolver o problema?
else {
tarefas[i] = tarefas[tarefas.size() - 1];
tarefas.pop_back();
}
return;
}
}void Projecto::adiciona(Tarefa const& t)
{
if(t.inicio() <= dia_actual)
cerr << "O início da nova tarefa deve posterior à data actual."
<< endl;
else
tarefas.push_back(t);
// Que faria para evitar inserção de tarefas com identificadores repetidos?
}inline void Projecto::avancaDia() {
++dia_actual;
}void Projecto::mostra() const
{
for(Tamanho i = 0; i != tarefas.size(); ++i) {
cout << "----------------------------------------" << endl;
cout << "Tarefa: " << lista_de_tarefas[i].identificacao() << endl;
cout << "Início: " << lista_de_tarefas[i].inicio() << endl;
cout << "Duração: " << lista_de_tarefas[i].duracao() << endl;
cout << "Estado: ";
if(tarefas[i].inicio() > dia_actual)
cout << "por iniciar" << endl;
else if (tarefas[i].inicio() +
tarefas[i].duracao() <= dia_actual)
cout << "terminada" << endl;
else
cout << "em curso" << endl;
}
cout << "----------------------------------------" << endl;
}bool Projecto::concluido() const {
for(Tamanho i = 0; i != tarefas.size(); ++i)
if(tarefas[i].inicio() + tarefas[i].duracao() > dia_actual)
return false;
return true;
}int main()
{
Projecto p;cout << "Indique o número de tarefas do projecto: ";
int numero_de_tarefas;
cin >> numero_de_tarefas;for(int i = 0; i != numero_de_tarefas; ++i) {
cout << "Identificação da tarefa: ";
string identificacao;
cin >> identificacao;cout << "Inicio: ";
int inicio;
cin >> inicio;cout << "Duração: ";
int duracao;
cin >> duracao;p.adiciona(Tarefa(identificacao, inicio, duracao));
}p.avancaDia();
while(!p.concluido()) {
int opcao;
do {
cout << "1 - Adicionar tarefa" << endl;
cout << "2 - Remover tarefa" << endl;
cout << "3 - Próximo dia" << endl;
cout << endl << "Opção: " << endl;
cin >> opcao;
switch(opcao) {
case 1:
cout << "Identificação da tarefa: ";
string identificacao;
cin >> identificacao;cout << "Inicio: ";
int inicio;
cin >> inicio;cout << "Duração: ";
int duracao;
cin >> duracao;p.adiciona(Tarefa(identificacao, inicio, duracao));
break;case 2:
cout << "Identificação da tarefa: ";
string identificacao;
cin >> identificacao;p.remove(identificacao);
break;case 3:
p.avancaDia();
break;
}
} while(opcao != 3);
p.mostra();
}
}
Defina completamente uma classe (ListaDePalavras) que permite manipular um texto de modo a obter listagem das palavras que o constituem. Deve declarar a classe e definir:
Supondo que o ficheiro texto.txt tem o seguinte conteúdo:
isto é um testeO seguinte troço de programa
claro que poderia não ser um teste
mas é um
ListaDePalavras l1("texto.txt");escreve no ecrã:l1.mostra();
istoSe o conteúdo de texto2.txt for:
é
um
teste
claro
que
poderia
não
ser
mas
isto é o segundo testeO seguinte troço de programa
poderia haver outros
mas não há
ListaDePalavras l1("texto.txt");teria como resultado:
ListaDePalavras l2("texto2.txt");
l1 += l2;l1.mostra();
isto[cotação: 3,5]
é
um
teste
claro
que
poderia
não
ser
mas
o
segundo
poderia
haver
outros
há
#include <iostream>Questão 4
#include <string>
#include <vector>
#include <fstream>using namespace std;
class ListaDePalavras {
public:
typedef vector<string>::size_type Tamanho;explicit ListaDePalavras(string const&);
void mostra() const;ListaDePalavras& operator += (ListaDePalavras const&);
private:
vector<string> palavras;
};ListaDePalavras::ListaDePalavras(string const& nome_do_ficheiro)
{
ifstream entrada(nome_do_ficheiro.c_str());
if(!entrada) {
cerr << "Erro: não foi possível abrir o ficheiro <"
<< nome_do_ficheiro << ">." << endl;
exit(1);
}string palavra;
while(entrada >> palavra) {
Tamanho i = 0;
while(i != palavras.size() && palavras[i] != palavra)
++i;
if(i == palavras.size())
palavras.push_back(palavra);
}
}void ListaDePalavras::mostra() const
{
for(Tamanho i = 0; i != palavras.size(); ++i)
cout << palavras[i] << endl;
}ListaDePalavras& ListaDePalavras::operator += (ListaDePalavras const& l)
{
for(Tamanho i = 0; i != l.palavras.size(); ++i) {
Tamanho j = 0;
while(j != palavras.size() && palavras[j] != l.palavras[i])
++j;
if(j == palavras.size())
palavras.push_back(l.palavras[i]);
}return *this;
}
Considere a função int decimal(const int m[], const int n) que devolve o inteiro correspondente ao número na base 8 recebido como uma matriz de dígitos de tamanho n. Para compreender melhor como se interpreta um número na base 8, atente no seguinte exemplo:
(271)8 = 185A função deve poder ser usada como se segue:(271) 8 = 2×82 + 7×81 + 1×80 = 2×64 + 7×8 + 1×1 = 128 + 56 + 1 = 185
int m[3] = {2, 7, 1};que escreve no ecrã
int valor = decimal(m, 3);
cout << "O valor é: " << valor << '.' << endl;
O valor é 185.Admita que está disponível a seguinte função:
// PC: n >= 04.1 Dada a pré-condição n > 0, indique a condição objectivo (CO), a condição invariante (CI) e a guarda (G) do ciclo necessário à construção desta função.
// CO: potência = xn
int potência(const int x, const int n)
{
int potência = 1;
for(int i = 0; i != n; ++i)
potência *= x;
return potência;
}
[cotação: 0,5]
CO: soma = (S j : 0 <= j < n : m[j] × 8n - 1 - j)4.2 Defina completamente a função indicada.
CI: soma = (S j : 0 <= j < i : m[j] × 8n - 1 - j) e 0 <= i <= n
G: i <> n
[cotação: 1]
int converteOctal(int const m[], int const n)4.3 Prove que o ciclo está correcto verificando que:
{
int soma = 0;
for(int i = 0; i != n; ++i)
soma += m[i] * potencia(8, n - 1 - i);return soma;
}
A demonstração pode ser feita de uma forma directa, deduzindo as asserções verdadeiras após cada instrução:
// CI e
G: soma = (S j : 0 <= j <
i
: m[j] × 8n - 1 - j) e
0 <= i <= n e i <>
n,
ou seja,
// soma = (S j
: 0 <= j <
i : m[j] × 8n
- 1 - j) e 0 <= i < n
soma += m[i] * potencia(8,
n - 1 - i);
// soma = (S j
: 0 <= j <
i : m[j] × 8n
- 1 - j) + m[i] × 8n
- 1 - i e 0 <= i < n, ou
seja,
// soma = (S j
: 0 <= j <
i + 1 : m[j] × 8n
- 1 - j) e 0 <= i < n
++i;
// soma = (S j
: 0 <= j <
i : m[j] × 8n
- 1 - j) e 0 <= i - 1 < n,
ou seja,
// soma = (S j
: 0 <= j <
i : m[j] × 8n
- 1 - j) e 1 <= i < n + 1,
ou seja,
// soma = (S j
: 0 <= j <
i : m[j] × 8n
- 1 - j) e 1 <= i <= n
// que implicaCI:
soma = (S j : 0 <= j <
i :
m[j] × 8n - 1 - j) e
0 <= i <= n.
Questão 5
Explique brevemente o que é a Condição Invariante de Instância (CII) de uma classe (lembre-se da classe Racional). Para que serve?
[cotação: 1]
Ver Folhas teóricas, Secção 6.3.4 (Condição Invariante de Instância).