Pular para conteúdo

Lista 03 · Structs e enum class

Exercícios da Semana 3 — Structs e enum class. Aqui você cria seus próprios tipos com struct e começa a escrever métodos dentro deles — é o primeiro passo em direção às classes da Unidade 5.

Como resolver

Copie o bloco de código de cada exercício, cole no seu compilador online, preencha a parte marcada como ESCREVA SUA SOLUCAO AQUI e rode. A saída mostra quais testes passaram.

ex01

Complete a struct Intervalo (campos inicio e fim, ambos double, já declarados) implementando os métodos duracao() e contem(double valor), ambos const.

  • duracao() devolve fim - inicio.
  • contem(valor) devolve true se o valor estiver dentro do intervalo, contando as duas pontas: inicio <= valor && valor <= fim.

Repare que os dois métodos são const: eles respondem perguntas sobre o intervalo sem alterá-lo. Marcar isso não é firula — é o que permite chamá-los sobre um const Intervalo&, e é o que avisa a quem lê que a struct sai da chamada exatamente como entrou.

contem precisa de duas comparações ligadas por &&. Escrever inicio <= valor <= fim compila em C++ e dá a resposta errada: o compilador lê isso como (inicio <= valor) <= fim, ou seja, compara um true/false com um double.

Intervalo aula{8.0, 10.0};
aula.duracao();       // -> 2.0
aula.contem(9.0);     // -> true
aula.contem(8.0);     // -> true   (a ponta conta)
aula.contem(11.0);    // -> false
// ex01 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX01_H
#define EX01_H

struct Intervalo {
    double inicio;
    double fim;

    double duracao() const;
    bool contem(double valor) const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
double Intervalo::duracao() const {
    // TODO: retorne fim - inicio.
    return 0.0;
}

bool Intervalo::contem(double valor) const {
    // TODO: retorne true se valor estiver entre inicio e fim,
    // incluindo as duas pontas. Sao DUAS comparacoes, ligadas por &&.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Intervalo aula{8.0, 10.0};
    assert(std::abs(aula.duracao() - 2.0) <= 0.0001);

    assert(aula.contem(9.0));
    assert(aula.contem(8.0));    // a ponta de baixo conta
    assert(aula.contem(10.0));   // a ponta de cima tambem
    assert(!aula.contem(7.9));
    assert(!aula.contem(11.0));

    // intervalo com inicio negativo
    Intervalo temperatura{-5.0, 5.0};
    assert(std::abs(temperatura.duracao() - 10.0) <= 0.0001);
    assert(temperatura.contem(0.0));
    assert(temperatura.contem(-5.0));
    assert(!temperatura.contem(-6.0));

    // intervalo de duracao zero: contem apenas o proprio ponto
    Intervalo instante{3.0, 3.0};
    assert(std::abs(instante.duracao() - 0.0) <= 0.0001);
    assert(instante.contem(3.0));
    assert(!instante.contem(3.1));

    // os metodos sao const: precisam funcionar sobre uma referencia const
    const Intervalo& constante = aula;
    assert(constante.contem(9.0));

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex02

Dado o enum class Turno { Manha, Tarde, Noite } (já declarado), escreva nome_turno, que devolve o nome por extenso de cada valor.

nome_turno(Turno::Manha)  // -> "Manha"
nome_turno(Turno::Tarde)  // -> "Tarde"
nome_turno(Turno::Noite)  // -> "Noite"
// ex02 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX02_H
#define EX02_H

enum class Turno { Manha, Tarde, Noite };

// Retorna o nome por extenso do turno.
std::string nome_turno(Turno t);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::string nome_turno(Turno t) {
    // TODO: use um if/else (ou switch) comparando t com Turno::Manha, Turno::Tarde, Turno::Noite.
    return "";
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(nome_turno(Turno::Manha) == std::string("Manha"));
    assert(nome_turno(Turno::Tarde) == std::string("Tarde"));
    assert(nome_turno(Turno::Noite) == std::string("Noite"));
    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex03

Escreva min_max, que recebe três inteiros e devolve um std::pair<int, int> com o menor valor em .first e o maior em .second.

min_max(3, 9, 5)   // -> {3, 9}
min_max(-1, -5, 0) // -> {-5, 0}
// ex03 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <utility>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX03_H
#define EX03_H

// Retorna {menor, maior} entre os tres valores.
std::pair<int, int> min_max(int a, int b, int c);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
std::pair<int, int> min_max(int a, int b, int c) {
    // TODO: monte um pair {menor, maior} entre a, b e c.
    return {0, 0};
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    auto r1 = min_max(3, 9, 5);
    assert(r1.first == 3);
    assert(r1.second == 9);

    auto r2 = min_max(-1, -5, 0);
    assert(r2.first == -5);
    assert(r2.second == 0);

    auto r3 = min_max(4, 4, 4);
    assert(r3.first == 4);
    assert(r3.second == 4);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex04

Escreva converte_para_int, que recebe um texto e o converte para inteiro. Como a conversão pode simplesmente não ter resposta (o texto pode não ser um número), a função devolve bool dizendo se deu certo, e escreve o número num parâmetro por referência — o padrão da Aula 02.

As regras do que é válido:

  • pode começar com um -, indicando número negativo (mas um - sozinho não é número);
  • o resto tem que ser só dígitos, pelo menos um;
  • texto vazio, com espaços, com letras ou com ponto decimal é inválido.

Quando o texto for inválido, devolva false e não mexa no parâmetro de saída.

Para converter, percorra os dígitos da esquerda para a direita acumulando resultado = resultado * 10 + digito. Um char de dígito vira o número que ele representa com c - '0': '7' - '0' dá 7, porque os dígitos são consecutivos na tabela de caracteres. Para testar se um char é dígito, c >= '0' && c <= '9'.

Não use std::stoi: ela aborta o programa em vez de avisar, e aceita coisas como "12abc" (converte os 12 e ignora o resto) — exatamente o que este exercício quer rejeitar.

int valor;
converte_para_int("42", valor)     // -> true, e valor vale 42
converte_para_int("-7", valor)     // -> true, e valor vale -7
converte_para_int("007", valor)    // -> true, e valor vale 7
converte_para_int("12a", valor)    // -> false
converte_para_int("", valor)       // -> false
converte_para_int("-", valor)      // -> false
// ex04 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX04_H
#define EX04_H

// Converte texto para int. Aceita um '-' inicial seguido de pelo menos um
// digito, e nada alem de digitos depois disso.
// Devolve true e escreve em "valor" se o texto for um inteiro valido;
// devolve false e nao mexe em "valor" caso contrario.
bool converte_para_int(const std::string& texto, int& valor);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool converte_para_int(const std::string& texto, int& valor) {
    // TODO:
    // 1. texto vazio -> devolva false.
    // 2. se comecar com '-', guarde que e negativo e comece a ler do indice 1;
    //    senao comece do indice 0. Se nao sobrar nenhum caractere, false.
    // 3. percorra os caracteres restantes: se algum nao for digito
    //    (c >= '0' && c <= '9'), devolva false;
    //    senao faca resultado = resultado * 10 + (c - '0').
    // 4. escreva o resultado com o sinal em "valor" e devolva true.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    int v = 0;

    assert(converte_para_int("42", v));
    assert(v == 42);
    assert(converte_para_int("0", v));
    assert(v == 0);
    assert(converte_para_int("7", v));
    assert(v == 7);

    // zeros a esquerda sao aceitos
    assert(converte_para_int("007", v));
    assert(v == 7);

    // negativos
    assert(converte_para_int("-7", v));
    assert(v == -7);
    assert(converte_para_int("-1234", v));
    assert(v == -1234);

    // invalidos: devolvem false e nao mexem no parametro de saida
    v = 99;
    assert(!converte_para_int("", v));
    assert(v == 99);
    assert(!converte_para_int("-", v));
    assert(!converte_para_int("12a", v));
    assert(!converte_para_int("a12", v));
    assert(!converte_para_int("1 2", v));
    assert(!converte_para_int(" 12", v));
    assert(!converte_para_int("3.14", v));
    assert(!converte_para_int("--5", v));
    assert(!converte_para_int("5-", v));
    assert(v == 99);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex05

Complete a struct Aluno (campos nome e nota, já declarados) implementando o método aprovado() const, que devolve true se nota >= 6.0.

Aluno a{"Carlos", 7.5};
a.aprovado();  // -> true

Aluno b{"Marcia", 5.0};
b.aprovado();  // -> false
// ex05 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX05_H
#define EX05_H

struct Aluno {
    std::string nome;
    double nota;

    bool aprovado() const;
};

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool Aluno::aprovado() const {
    // TODO: retorne true se nota >= 6.0.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Aluno a{"Carlos", 7.5};
    assert(a.aprovado());

    Aluno b{"Marcia", 5.0};
    assert(!(b.aprovado()));

    Aluno c{"Julia", 6.0};
    assert(c.aprovado());

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex06

Escreva busca_posicao, que recebe um texto e um caractere alvo, e devolve a posição (índice) da primeira ocorrência do caractere — ou -1 se o caractere não aparecer.

O -1 funciona bem aqui por um motivo específico: nenhuma posição válida é negativa, então -1 nunca se confunde com uma resposta de verdade. Guarde essa observação; ela não vale para toda função.

busca_posicao("programacao", 'g')  // -> 3
busca_posicao("programacao", 'z')  // -> -1
// ex06 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX06_H
#define EX06_H

// Retorna a posicao da primeira ocorrencia de "alvo" em "texto",
// ou -1 se o caractere nao aparecer.
int busca_posicao(const std::string& texto, char alvo);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int busca_posicao(const std::string& texto, char alvo) {
    // TODO: percorra "texto"; se achar "alvo", retorne o indice.
    // Se terminar o laco sem achar, retorne -1.
    return -1;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(busca_posicao("programacao", 'g') == 3);
    assert(busca_posicao("programacao", 'z') == -1);
    assert(busca_posicao("abc", 'a') == 0);
    assert(busca_posicao("", 'a') == -1);

    // devolve a PRIMEIRA ocorrencia
    assert(busca_posicao("banana", 'a') == 1);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex07

Dada a struct Intervalo (campos inicio e fim, ambos double, já declarada — a mesma do ex01), escreva intersecao, que recebe dois intervalos e escreve num terceiro parâmetro a parte em que os dois se sobrepõem. A função devolve bool: true se houve sobreposição, false se os intervalos não se encontram em ponto nenhum.

Repare no parâmetro de saída: até agora você devolveu números por referência. Aqui o que volta é uma struct inteira, e o mecanismo é o mesmo — referência funciona com qualquer tipo, inclusive os que você mesmo criou.

A interseção começa no maior dos dois inícios e termina no menor dos dois fins. Se essa conta produzir um início maior que o fim, é porque os intervalos não se sobrepõem — devolva false e não mexa no parâmetro de saída. Dois intervalos que se tocam em um ponto só (um termina exatamente onde o outro começa) contam como sobreposição, de duração zero.

Resolva com comparações escritas na mão (if ou operador ternário) — std::max e std::min moram em <algorithm>, que é assunto do capítulo 8.

Intervalo aula{8.0, 10.0};
Intervalo reuniao{9.0, 11.0};
Intervalo r;
intersecao(aula, reuniao, r)   // -> true, e r vale {9.0, 10.0}

Intervalo tarde{14.0, 16.0};
intersecao(aula, tarde, r)     // -> false
// ex07 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <cmath>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX07_H
#define EX07_H

struct Intervalo {
    double inicio;
    double fim;
};

// Escreve em "resultado" a parte em que os dois intervalos se sobrepoem.
// Devolve true se ha sobreposicao; devolve false e nao mexe em "resultado"
// se eles nao se sobrepoem em ponto nenhum.
bool intersecao(const Intervalo& a, const Intervalo& b, Intervalo& resultado);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool intersecao(const Intervalo& a, const Intervalo& b, Intervalo& resultado) {
    // TODO: o inicio da intersecao e o MAIOR entre a.inicio e b.inicio;
    // o fim e o MENOR entre a.fim e b.fim.
    // Se o inicio calculado for maior que o fim, nao ha sobreposicao:
    // devolva false. Caso contrario, escreva o Intervalo em "resultado"
    // e devolva true.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    Intervalo r{0.0, 0.0};

    // sobreposicao parcial
    assert(intersecao(Intervalo{8.0, 10.0}, Intervalo{9.0, 11.0}, r));
    assert(std::abs(r.inicio - 9.0) <= 0.0001);
    assert(std::abs(r.fim - 10.0) <= 0.0001);

    // a ordem dos argumentos nao muda o resultado
    assert(intersecao(Intervalo{9.0, 11.0}, Intervalo{8.0, 10.0}, r));
    assert(std::abs(r.inicio - 9.0) <= 0.0001);
    assert(std::abs(r.fim - 10.0) <= 0.0001);

    // um intervalo inteiramente dentro do outro
    assert(intersecao(Intervalo{0.0, 24.0}, Intervalo{8.0, 10.0}, r));
    assert(std::abs(r.inicio - 8.0) <= 0.0001);
    assert(std::abs(r.fim - 10.0) <= 0.0001);

    // sem sobreposicao nenhuma: devolve false e nao mexe no resultado
    r = Intervalo{-1.0, -1.0};
    assert(!intersecao(Intervalo{8.0, 10.0}, Intervalo{14.0, 16.0}, r));
    assert(std::abs(r.inicio + 1.0) <= 0.0001);
    assert(!intersecao(Intervalo{14.0, 16.0}, Intervalo{8.0, 10.0}, r));

    // tocam em um ponto so: conta, com duracao zero
    assert(intersecao(Intervalo{8.0, 10.0}, Intervalo{10.0, 12.0}, r));
    assert(std::abs(r.inicio - 10.0) <= 0.0001);
    assert(std::abs(r.fim - 10.0) <= 0.0001);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex08

Dados o enum class Prioridade { Baixa, Media, Alta } e a struct Tarefa (campos titulo, prioridade e minutos, já declarados), escreva minutos_reservados, que devolve quanto tempo separar para a tarefa, segundo a prioridade:

  • Alta → o dobro de minutos;
  • Media → exatamente minutos;
  • Baixa → a metade de minutos (divisão inteira: 45 vira 22).

Use um switch sobre t.prioridade. O erro que quase todo mundo comete na primeira tentativa é escrever case Alta: — com enum class, o nome mora dentro do tipo, então é case Prioridade::Alta:.

minutos_reservados(Tarefa{"entregar relatorio", Prioridade::Alta, 30})   // -> 60
minutos_reservados(Tarefa{"responder e-mails", Prioridade::Media, 30})   // -> 30
minutos_reservados(Tarefa{"organizar mesa", Prioridade::Baixa, 45})      // -> 22
// ex08 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX08_H
#define EX08_H

enum class Prioridade { Baixa, Media, Alta };

struct Tarefa {
    std::string titulo;
    Prioridade prioridade;
    int minutos;
};

// Retorna quantos minutos reservar para a tarefa:
// Alta = o dobro de minutos, Media = minutos, Baixa = metade (divisao inteira).
int minutos_reservados(const Tarefa& t);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
int minutos_reservados(const Tarefa& t) {
    // TODO: use um switch sobre t.prioridade.
    // Lembre do qualificador: e "case Prioridade::Alta:", nao "case Alta:".
    return 0;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    assert(minutos_reservados(Tarefa{"entregar relatorio", Prioridade::Alta, 30}) == 60);
    assert(minutos_reservados(Tarefa{"responder e-mails", Prioridade::Media, 30}) == 30);
    assert(minutos_reservados(Tarefa{"organizar mesa", Prioridade::Baixa, 45}) == 22);

    // metade de um numero par, e os casos de borda com zero
    assert(minutos_reservados(Tarefa{"alongar", Prioridade::Baixa, 10}) == 5);
    assert(minutos_reservados(Tarefa{"nada", Prioridade::Alta, 0}) == 0);
    assert(minutos_reservados(Tarefa{"nada", Prioridade::Baixa, 1}) == 0);

    std::cout << "Todos os testes passaram!\n";
    return 0;
}

ex09

Escreva primeiro_repetido, que recebe um texto e escreve num parâmetro por referência o primeiro caractere que já tinha aparecido antes nele. A função devolve bool: true se encontrou uma repetição, false se todos os caracteres forem distintos.

Atenção ao que "primeiro" significa aqui: é o primeiro caractere que, ao ser lido da esquerda para a direita, já tinha sido visto. Em "programacao", o r da posição 4 é a primeira repetição, mesmo que o o apareça mais vezes no total.

Repare que o que volta é o caractere, e não a posição. Quando a resposta natural da pergunta é o próprio valor, não há motivo para devolver o índice e obrigar quem chamou a ir buscar o caractere de volta. E repare por que aqui não serve um valor especial como o -1 do ex06: qualquer char é uma resposta possível, então não sobra nenhum valor para significar "não achei" — é por isso que esta função devolve bool à parte.

char c;
primeiro_repetido("programacao", c)   // -> true, e c vale 'r'
primeiro_repetido("bola", c)          // -> false
primeiro_repetido("", c)              // -> false
// ex09 — versao de arquivo unico (gerada automaticamente,
// nao edite este arquivo direto: edite os fontes em exercicios/ e rode
// tools/gerar_single.py --todos de novo).
//
// Cole este arquivo inteiro no seu compilador online e rode.

#include <cassert>
#include <iostream>
#include <string>

// ============================================================
// Declaracoes do exercicio
// ============================================================
#ifndef EX09_H
#define EX09_H

// Escreve em "repetido" o primeiro caractere de "texto" que ja havia
// aparecido antes. Devolve true se encontrou repeticao; devolve false e nao
// mexe em "repetido" se todos os caracteres forem distintos.
bool primeiro_repetido(const std::string& texto, char& repetido);

#endif

// ============================================================
// ESCREVA SUA SOLUCAO AQUI
// ============================================================
bool primeiro_repetido(const std::string& texto, char& repetido) {
    // TODO: para cada posicao i, verifique se texto[i] ja apareceu
    // em alguma posicao anterior (um laco de j = 0 ate i).
    // Se ja apareceu, escreva texto[i] em "repetido" e devolva true.
    // Se o laco terminar, devolva false.
    return false;
}

// ============================================================
// NAO ALTERE — testes
// ============================================================
int main() {
    char c = '?';

    assert(primeiro_repetido("programacao", c));
    assert(c == 'r');

    // todos distintos: devolve false e nao mexe no parametro de saida
    c = '?';
    assert(!primeiro_repetido("bola", c));
    assert(c == '?');

    // texto vazio e texto de um caractere so
    assert(!primeiro_repetido("", c));
    assert(!primeiro_repetido("a", c));

    // repeticao logo no comeco
    assert(primeiro_repetido("aa", c));
    assert(c == 'a');

    // a primeira repeticao nao e a do caractere mais frequente
    assert(primeiro_repetido("abcbb", c));
    assert(c == 'b');

    std::cout << "Todos os testes passaram!\n";
    return 0;
}