Critérios para aprovação
Em uma disciplina, há avaliações de tarefas práticas e tarefas teóricas. Cada estudante recebe uma média aritmética P de 3 tarefas práticas e uma média aritmética T de 2 tarefas teóricas. A média final é dada por F = (T + P)/2.
Devido ao baixo desempenho da turma nas tarefas teóricas, o professor decidiu aplicar a seguinte regra de normalização: uma nova nota T' será dada pela nota T original vezes 10 e dividida pela maior nota T entre todos estudantes da turma. Assim, se João tirou 3 e a maior nota foi 6, então a nova nota de João será 10*3/6, ou 5.
Devido às inúmeras reclamações da turma, o professor resolveu ainda dar 10% de bônus na nota P de cada estudante. Assim, a nova nota P' será 1,1*P.
Seu objetivo é calcular a nota final de cada estudante e a médias das novas notas T' e P'.
Entrada
A entrada tem:
- um número
n
de estudante - uma sequência de
n
linhas, cada uma com as três notas das tarefas práticas, em ordem de RA - uma sequência de
n
linhas, cada uma com as duas notas das tarefas teóricas, em ordem de RA
Exemplo:
2 5 6 7 7 7 10 2 4 5 7
Saída
A saída deve conter
n
linhas, cada uma com a nota final de cada estudante- em seguida, duas linhas com as notas máximas de T e P entre todos estudantes
- por último, duas linhas com as médias de T e P
Para a entrada acima, obtemos
5.8 9.4 Max P: 8.8 Max T: 10 Media P: 7.7 Media T: 7.5
Pois sabemos que as médias práticas dos dois estudantes forma um vetor P = [6, 8], mas como aumentamos 10%, obtemos P' = [6.6, 8.8]. As médias teóricas formam um vetor T = [3, 6], mas como normalizamos, temos que multiplicá-las por 10/6, obtendo T' = [5, 10].
Assim, as médias máximas de P' e T' são 8.8 e 10, respectivamente. Também, as médias de P' e T' entre os dois estudantes são 7.7 e 7.5.
Calculando as notas finais de cada estudante, obtemos F = [5.8, 9.4].
Implementando
Essa tarefa parece bem difícil, mas na verdade ela só realiza operações bem simples. O que é complicado é que ela faz diversas coisas diferentes e se não nos organizarmos, podemos nos perder rapidamente. Para evitar isso, vamos listar as várias coisas diferentes que precisamos fazer:
- ler as notas práticas de cada estudante e guardar as médias em um vetor de médias práticas P
- ler as notas teóricas de cada estudante e guardar as médias em um vetor de médias teóricas T
- para normalizar as notas, precisamos descobrir o máximo do vetor T
- depois precisamos multiplicar o vetor T por 10 dividido por essa nota máxima
- também precisamos multiplicar P por um fator 1.1
- no final, precisamos calcular a média final de cada estudante
- calculamos o máximo de P e de T
- e ainda precisamos calcular a média de P e de T
É muita coisa!!!
Se tentarmos fazer tudo de uma vez só, vamos obter um código muito grande e difícil de ler. Pior, se houver um erro, vamos gastar um tempo enorme tentando descobrir onde ele está. Essa é a situação ideal para usarmos funções. Usamos funções porque:
-
Cada função tem uma responsabilidade bem pequena e podemos pensar em um problema menor de cada vez. Por isso, é muito mais fácil escrever e testar uma função separadamente.
-
Podemos chamar a mesma função múltiplas vezes. Isso evita duplicidade de código, deixa o código menor, mais simples e mais flexível.
Por onde começamos?
É sempre bom começar a escrever um programa pela leitura dos dados e pelas funções mais simples. Vamos fazer duas funções, uma para ler as notas práticas e uma para ler as notas teóricas.
def ler_notas_praticas(n): P = [] for _ in range(n): notas = input().split() nota_pratica = 0 for nota in notas: nota_pratica += float(nota) nota_pratica = nota_pratica / 3 P.append(nota_pratica) return P def ler_notas_teoricas(n): T = [] for _ in range(n): notas = input().split() nota_teorica = 0 for nota in notas: nota_teorica += float(nota) nota_teorica = nota_teorica / 2 T.append(nota_teorica) return T n = int(input()) P = ler_notas_praticas(n) T = ler_notas_teoricas(n)
Um leitor atento vai perceber que ambas funções precisam ler os números de uma linha e tirar a média entre eles, mas a primeira lê 3 números, enquanto a segunda lê apenas dois. Esse é um caso de duplicidade de código. Podemos simplificar isso da seguinte maneira
def ler_media(m): notas = input().split() media = 0 for nota in notas: media += float(nota) media = media / m return media def ler_notas_praticas(n): P = [] for _ in range(n): P.append(ler_media(3)) return P def ler_notas_teoricas(n): T = [] for _ in range(n): T.append(ler_media(2)) return T n = int(input()) P = ler_notas_praticas(n) T = ler_notas_teoricas(n)
Para escrevermos funções correspondentes em C, precisamos descobrir como definir
uma função em C. Primeiro, vamos construir uma função ler_media
. Observe que
ela deve receber um parâmetro inteiro m
e deve devolver um valor númerico
fracionário do tipo float
. Em C, todas as variáveis devem ter um tipo
declarado e isso também vale para os parâmetros e valor de retorno de uma
função.
#include <stdio.h> float ler_media(int m) { int i; float media, nota; media = 0; for (i = 0; i < m; i++) { scanf("%f", ¬a); media += nota; } media = media / m; return media; } int main() { float media; media = ler_media(3); printf("%f\n", media); }
Repare o tipo float
antes do nome da função. Ele está indicando que essa
função deve retornar um valor do tipo float
. Se escrevêssemos algo diferente,
como return "Olá";
dentro do corpo da função, o compilador iria se recusar a
continuar porque o tipo "Olá"
é diferente do tipo de retorno esperado.
Observe também que escrevemos uma função main
bem simples que apenas imprime
uma média. Fizemos isso para poder testar a função ler_media
: é sempre bom
testar cada uma das funções que construímos separadamente, à medida em que
escrevemos.
Devolvendo um vetor por função
Agora temos que escrever a função ler_notas_praticas
, mas essa função devolve
uma lista de números fracionários do tipo float
. Acontece que em C uma função
não pode retornar um vetor. Ao invés disso, temos que passar um vetor por
parâmetro e alterá-lo dentro da função. Como a função não irá mais retornar o
vetor, anotamos o seu tipo como void
, para indicar que ela não devolve nada.
#include <stdio.h> #define MAX_ALUNOS 100 void ler_notas_praticas(float P[MAX_ALUNOS], int n) { int i; /* índice da posição do vetor */ for (i = 0; i < n; i++) { P[i] = ler_media(3); } } int main() { int n; float P[MAX_ALUNOS]; scanf("%d", &n); ler_notas_praticas(P, n); }
A função ler_notas_teoricas
é completamente análoga.
Recebendo um vetor por função
Revendo nossa lista de passos, notamos que devemos obter o valor máximo de um
vetor repetidas vezes. Em Python podemos usar a função interna max
, ou
implementar nossa próxima função obter_maximo
.
def obter_maximo(vetor): maximo = vetor[0] for valor in vetor: if maximo < valor: maximo = valor return maximo
Em C, uma função recebe um vetor da mesma forma que devolve um vetor: passando por parâmetro.
float obter_maximo(float vetor[MAX_ALUNOS], int n) { int i; float maximo = vetor[0]; for (i = 0; i < n; i++) { if (maximo < vetor[i]) maximo = vetor[i]; } return maximo; } int main() { int n; float P[MAX_ALUNOS]; float maximo; scanf("%d", &n); ler_notas_praticas(P, n); maximo = obter_maximo(P, n); printf("Max P: %f\n", maximo); }
Devemos observar que dessa vez precisamos informar o número de elementos usados
no vetor. Em C, o número de elementos usados em um vetor não é guardado no
próprio vetor como acontece com as listas em Python, então precisamos sempre
manter o número n
de posições ocupadas do vetor.
Alterando um vetor por função
Continuando a nossa lista, percebemos que também precisamos de uma função para multiplicar os valores atuais de um vetor por um certo fator. Nesse caso, o vetor é tanto entrada quanto saída da função.
def mutiplicar_fator(vetor, fator): for i in range(len(v)): v[i] = v[i] * fator n = int(input()) P = ler_notas_praticas(n) mutiplicar_fator(P, 1.1)
Em C, fazemos algo como
void mutiplicar_fator(float vetor[MAX_ALUNOS], int n, float fator) { int i; for (i = 0; i < n; i++) { vetor[i] = vetor[i] * fator; } } int main() { int n; float P[MAX_ALUNOS]; float maximo; scanf("%d", &n); ler_notas_praticas(P, n); mutiplicar_fator(P, n, 1.1); maximo = obter_maximo(P, n); printf("Max P: %f\n", maximo); }
Dessa vez, o programa irá imprimir o valor máximo das notas práticas já com o bônus de 10%.
Resolvendo o exercício
Ainda precisamos construir outras funções, como obter_media
, que devolve a
média dos valores de um vetor, e imprimir_notas
, que recebe os dois vetores P
e T e imprime as médias finais dos estudantes. Com essas funções, só restará
organizar todas as chamadas de função em um lugar só. Em Python, teríamos algo
como
# ... funções... n = int(input()) P = ler_notas_praticas(n) T = ler_notas_teoricas(n) mutiplicar_fator(P, 1.1) maximo_teorica = obter_maximo(T) mutiplicar_fator(T, 10.0/maximo_teorica) imprimir_notas(P, T) maximo_pratica = obter_maximo(P) maximo_teorica = obter_maximo(T) media_pratica = obter_media(P) media_teorica = obter_media(T) print("Max P:", maximo_pratica) print("Max T:", maximo_teorica) print("Media P:", media_pratica) print("Media T:", media_teorica)
Reescreva agora esse programa em C, colocando todas essas chamadas de função na
função main
e escrevendo as funções que faltam. Você pode começar com o código
acima e terminá-lo em Critérios para
Aprovação
Exemplos relacionados
É opcional indicar o tamanho máximo do vetor nas declarações de parâmetro de função. Isso é útil porque podemos passar vetores de diferentes capacidades para a mesma função, desde que os vetores tenha o mesmo tipo.
#include <stdio.h> /* não precisamos da capacidade no parâmetro */ int minimo(int vetor[], int n) { int i; int min = vetor[0]; for (i = 0; i < n; i++) { if (min > vetor[i]) { min = vetor[i]; } } return min; } int main() { int grande[100]; int pequeno[5]; /* ...inicializa todas posições dos vetores... */ if (minimo(grande, 100) > minimo(pequeno, 5)) { printf("O menor elemento de todos está em pequeno\n"); } else { printf("O menor elemento de todos está em grande\n"); } }