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");
}
}