SlideShare ist ein Scribd-Unternehmen logo
1 von 483
Downloaden Sie, um offline zu lesen
Programação Imperativa
Lição n.º 1
Preliminares
Preliminares
•  Apresentação.
•  A programação na LEI.
•  O que é um computador?
•  O que é um programa?
•  Linguagens de programação.
•  A linguagem de programação C.
•  Bibliografia.
18/12/14 Programação Imperativa 2
Apresentação
•  Aulas teóricas às segundas-feiras, das 9:00 às
10:00 e às quintas-feiras, das 8:30 às 9:30, no
anfiteatro 1.8.1, no edifício 8.
•  Aulas práticas para várias turmas.
•  Professor das teóricas: Pedro Guerreiro.
•  Professoras das práticas: Margarida Madeira,
Noélia Correia e CristinaVieira.
•  Avaliação ao longo do funcionamento e exame
final.
•  Página na tutoria: http://goo.gl/1B56WO.
18/12/14 Programação Imperativa 3
A programação na LEI
•  Programação Imperativa.
•  Laboratório de Programação.
•  Programação Orientada por Objetos.
•  Algoritmos e Estruturas de Dados.
•  Bases de Dados.
•  Computação Gráfica.
•  Desenvolvimento de Aplicações para a Web.
•  Compiladores.
•  Inteligência Artificial.
•  ...
18/12/14 Programação Imperativa 4
A programação na LEI
•  Programação Imperativa.
•  Laboratório de Programação.
•  Programação Orientada por Objetos.
•  Algoritmos e Estruturas de Dados.
•  Bases de Dados.
•  Computação Gráfica.
•  Desenvolvimento de Aplicações para a Web.
•  Compiladores.
•  Inteligência Artificial.
•  ...
18/12/14 Programação Imperativa 5
O que é um computador?
18/12/14 Programação Imperativa 6
ENIAC (1946)
UNIVAC I (1951)
O que é um computador? (2)
18/12/14 Programação Imperativa 7
IBM 360 (1965)
DG Eclipse MV/8000 (1980)DECVAX-11/780 (1978)
PDP 11/70 (1975)
O que é um computador? (3)
18/12/14 Programação Imperativa 8
IBM PC 5150
(12 de Agosto de 1981)
Apple Macintosh
(24 de Janeiro de 1984)
O que é um computador? (4)
18/12/14 Programação Imperativa 9
Computador “torre” Computador “laptop”
O que é um computador? (5)
18/12/14 Programação Imperativa 10
Computador “torre” Computador “laptop”
Surface Pro 3 e Macbook Air
E ainda...
18/12/14 Programação Imperativa 11
Fonte: IEEE Standard Glossary of Computer
Hardware Terminology (1994).
I'm sorry, Dave. I'm
afraid I can't do that..
HAL 9000
“Definição” de computador
A device that consists of one or more associated
processing units and peripheral units, that is
controlled by internally stored programs, and that
can perform substantial computations, including
numerous arithmetic operations, or logic operations,
without human intervention during a run.
18/12/14 Programação Imperativa 12
Fonte: IEEE Standard Glossary of Computer
Hardware Terminology (1994).
O que é um programa?
•  Um programa é uma sequência de instruções
que um computador executará automatica-
mente, para levar a cabo uma determinada
tarefa.
•  As instruções são executadas sequencial-
mente, primeiro a primeira instrução do
programa, depois a segunda, e assim por diante
até ao fim do programa, exceto no caso das
instruções de salto, as quais permitem
“saltar” (condicionalmente ou não) para outra
instrução, mais à frente ou mais atrás.
18/12/14 Programação Imperativa 13
Como são os programas?
•  Os programas são texto, isto é, sequências de
frases, formadas por palavras, formadas por
carateres.
•  Os programas são escritos por pessoas ou
por outros programas.
•  Cada programa é escrito numa linguagem de
programação.
•  Os compiladores são programas que traduzem
um programa escrito numa linguagem para
outra linguagem que o computador é capaz de
processar mais eficientemente.
18/12/14 Programação Imperativa 14
Programação imperativa
•  A programação imperativa é um estilo de
programação que reflete a ideia fundamental
de que as instruções constituem ordens que o
computador deve cumprir: read, write, call, stop,
wait, add, connect, perform, etc.
•  À programação imperativa contrapõe-se a
programação funcional, para a qual um
programa é a descrição de uma função (no
sentido da matemática); executar o programa
é avaliar a função para argumentos dados, a
fim de obter os correspondentes resultados.
18/12/14 Programação Imperativa 15
Linguagens de programação
•  Os programas são escritos usando linguagens
de programação.
•  Cada linguagem de programação é um
conjunto de regras definidas inequivocamente
num documento de referência.
•  Há regras sintáticas (que exprimem as
maneiras válidas de escrever programas) e
regras semânticas (que exprimem o significado
operacional dos programas).
•  Há ainda regras de estilo, peculiares de cada
organização.
18/12/14 Programação Imperativa 16
Principais linguagens
18/12/14 Programação Imperativa 17
A linguagem de programação C
•  Em Programação Imperativa programaremos
em C.
•  A linguagem C foi inventada por
Dennis Ritchie, nos Laboratórios Bell, em
1972.
•  A linguagem C provém da linguagem B, a qual
provinha da linguagem BCPL, a qual provinha
da linguagem CPL, a qual provinha do Algol 60.
•  A linguagem C influenciou diretamente as
linguagens C++, Java, Objective C, C# e, mais
ou menos diretamente, muitas outras.
18/12/14 Programação Imperativa 18
Evolução do C
•  1972: invenção do C.
•  1989: normalização ANSI C, ou C89.
•  1990: normalização ISO C, ou C90, igual à
anterior.
•  1999: normalização ISO, C99.
•  2011: normalização ISO, C11.
18/12/14 Programação Imperativa 19
Bibliografia
18/12/14 Programação Imperativa 20
Programação Imperativa
Lição n.º 2
Programação com C
Programação com C
•  Problemas de programação.
•  Decomposição funcional.
•  Funções em C.
•  Funções de teste.
18/12/14 Programação Imperativa 22
Problemas de programação
•  Tipicamente, a tarefa de um programador é
escrever programas para realizar determinadas
tarefa, ou para resolver determinados
problemas.
•  Problema de hoje: escrever um programa C
para calcular a nota final em Programação
Imperativa, dada a nota da parte prática e a
nota do exame.
18/12/14 Programação Imperativa 23
Problema da nota final
•  A nota final é a média ponderada da nota da
parte prática e da nota do exame, com pesos
30% e 70%, respetivamente.
•  Mas se a nota do exame for menor que 8.5, a
nota final é a nota do exame.
•  As notas são expressas na escala de 0 a 20.
•  A notas da parte prática e do exame são
expressas com uma casa decimal.
•  A nota final é expressa na forma de um
número inteiro, obtido por arredondamento
do resultado dos cálculos.
18/12/14 Programação Imperativa 24
Funções identificadas no enunciado
•  A função para a média ponderada da nota da
parte prática e da nota do exame.
•  A função que se ocupa do caso em que a nota
do exame é menor do que 8.5.
•  A função que arredonda (para o número
inteiro mais próximo) o resultado dos
cálculos. (Esta é um exemplo das tais funções
gerais)
18/12/14 Programação Imperativa 25
Esta é um caso das tais
funções gerais.
Ambiente de programação
•  Programaremos escrevendo os nossos
programas num editor de texto e compilando
numa janela de comando.
•  Teremos numa janela o editor e noutra a
janela de comando:
18/12/14 Programação Imperativa 26
A janela de comando está
colocada na diretoria
onde guardamos os
programas.
Média ponderada
•  Se x representar a nota da prática e y a nota
do exame, a média ponderada desses dois
valores, usando os pesos 30% e 70%, é dada
pela expressão x * 0.3 + y * 0.7.
•  As variáveis x e y denotam números reais,
com parte decimal.
•  Em C, os números reais são representados
pelo tipo double.
•  A função para a média ponderada terá dois
argumentos de tipo double e o resultado
também é de tipo double.
18/12/14 Programação Imperativa 27
Função weighted_average
•  Observe:
•  Usamos aqueles nomes lab e exam para deixar
claro o significado dos argumentos.
18/12/14 Programação Imperativa 28
double weighted_average(double lab, double exam)
{
return lab * 0.3 + exam * 0.7;
}
O compilador dá erro,
indicando que o programa
não tem uma função main.
Função de teste
•  Escrevamos uma função de teste para
exercitar a função weighted_average:
18/12/14 Programação Imperativa 29
void test_weighted_average(void)
{
double lb;
double ex;
scanf("%lf%lf", &lb, &ex);
double z = weighted_average(lb, ex);
printf("%fn", z);
}
O compilador continuaria a
dar erro, porque continua a
faltar a função main.
Função main
•  A função main chama a função de teste:
18/12/14 Programação Imperativa 30
int main(void)
{
test_weighted_average();
return 0;
}
O compilador dá outro
erro agora (na verdade,
trata-se de um warning...)
e sugere que incluamos o
“header” <stdio.h>.
Programa completo
18/12/14 Programação Imperativa 31
#include <stdio.h>
double weighted_average(double lab, double exam)
{
return lab * 0.3 + exam * 0.7;
}
void test_weighted_average(void)
{
double lb;
double ex;
scanf("%lf%lf", &lb, &ex);
double z = weighted_average(lb, ex);
printf("%fn", z);
}
int main(void)
{
test_weighted_average();
return 0;
}
Este é o programa completo.
Tem uma função de cálculo, uma
função de teste e a função main.
À cabeça vem a diretiva #include
<stdio.h>.
Experimentando
•  Compilamos e corremos na janela de comando:
18/12/14 Programação Imperativa 32
sources pedro$ gcc -Wall nota_final.c
sources pedro$ ./a.out
10 12
11.400000
sources pedro$ ./a.out
15 18
17.100000
sources pedro$ ./a.out
17.2 14.5
15.310000
sources pedro$ ./a.out
14.8 7.1
9.410000
sources pedro$
De cada vez que corremos o
programa, só fazemos uma
experiência.
void test_weighted_average(void)
{
double lb;
double ex;
scanf("%lf%lf", &lb, &ex);
double z = weighted_average(lb, ex);
printf("%fn", z);
test_weighted_average();
}
Experimentando repetidamente
•  É simples: depois de escrever o resultado,
chamamos a função de teste, de novo:
18/12/14 Programação Imperativa 33
sources pedro$ ./a.out
12.9 10.0
10.870000
8.5 12.7
11.440000
14.8 12.0
12.840000
^C
sources pedro$
Paramos o programa,
interrompendo-o, com
ctrl-C.
Experimentando repetidamente, melhor
•  Em vez de interromper o programa à bruta,
com ctrl-C, é melhor deixar o programa seguir
quando acabarem os dados.
•  Neste caso, o programa seguirá, mas como não
há mais nada que fazer, terminará
imediatamente.
•  O fim dos dados é assinalado com ctrl-Z em
Windows e com ctrl-D em Linux/MacOS.
•  O ctrl-C é usado para interromper um
programa que disparatou ou um programa que
chamámos por engano, não para fazer um
programa terminar normalmente.18/12/14 Programação Imperativa 34
Ciclo de teste
•  Observe com muita atenção:
18/12/14 Programação Imperativa 35
void test_weighted_average(void)
{
double lb;
double ex;
while (scanf("%lf%lf", &lb, &ex) != EOF)
{
double z = weighted_average(lb, ex);
printf("%fn", z);
}
}
sources pedro$ ./a.out
12 15
14.100000
13.8 19.0
17.440000
10.1 19.9
16.960000
sources pedro$
Eu terei dado ctrl-D para
assinalar o fim dos dados,
mas o ctrl-D não é ecoado.
Função da nota exata
•  A média ponderada nem sempre dá a nota; só
dá quando a nota do exame é maior ou igual a
8.5
•  Caso contrário, o resultado é a nota do
exame.
•  Observe:
18/12/14 Programação Imperativa 36
double grade(double lab, double exam)
{
return exam >= 8.5
? weighted_average(lab, exam)
: exam;
}
Atenção aos operadores ponto
de interrogação e dois pontos!
Função de teste para a nota exata
•  Para controlo, incluímos também uma
chamada à função weighted_average:
18/12/14 Programação Imperativa 37
void test_grade(void)
{
double lb;
double ex;
while (scanf("%lf%lf", &lb, &ex) != EOF)
{
double v = weighted_average(lb, ex);
printf("%fn", v);
double z = grade(lb, ex);
printf("%fn", z);
}
}
A nova função main
•  A função main chama agora a nova função de
teste.
•  A anterior função de teste continua lá, mas
comentada:
18/12/14 Programação Imperativa 38
int main(void)
{
// test_weighted_average();
test_grade();
return 0;
}
Tipicamente, as funções main são assim:
chamam uma de várias funções de teste,
estando as outras comentadas, para poderem
facilmente ser reativadas, se necessário.
Experimentando a nota exata
•  Eis uma sessão de
experimentação,
usando a nova
função main:
18/12/14 Programação Imperativa 39
sources pedro$ ./a.out
14 10
11.200000
11.200000
16 8
10.400000
8.000000
16 8.4
10.680000
8.400000
16 8.5
10.750000
10.750000
19 6.2
10.040000
6.200000
sources pedro$
Confirmamos que nos
casos em que a nota do
exame é menor que 8.5, as
duas funções dão
resultados diferentes.
Conclusão
•  Já conseguimos calcular a nota exata, isto é, a
nota calculada com toda a precisão.
•  Falta calcular a nota final, arredondada.
•  Note que as funções presume, que os valores
dos argumentos fazem sentido, isto é, que são
números reais entre 0.0 e 20.0., expressos com
uma casa decimal, mas o programa não controla
isso, e calcula cegamente.
•  Aliás, se na função de teste fornecermos “lixo”,
isto é, sequências de carateres que não
constituem números decimais, o programa
estoira ingloriamente.18/12/14 Programação Imperativa 40
Programação Imperativa
Lição n.º 3
Operações aritméticas
Programação com C
•  Aritmética em C.
•  Aritmética int.
•  Aritmética double.
•  Aritmética mista.
•  Funções matemáticas de biblioteca.
•  Funções max e min.
18/12/14 Programação Imperativa 42
Aritmética em C
•  As regras da aritmética do C são semelhantes
às da aritmética da matemática, que
aprendemos na escola primária.
•  Mas há diferenças subtis, que frequentemente
nos apanham desprevenidos.
•  Primeira observação importante: os números
inteiros são representados pelo tipo int, mas o
tipo int não representa todos os números
inteiros!
•  Só representa os número inteiros do intervalo
[-2147483648..2147483647].
18/12/14 Programação Imperativa 43
Testando a adição de ints
•  Eis um programa com uma função de teste que faz
repetidamente a adição de dois números int:
18/12/14 Programação Imperativa 44
#include <stdio.h>
void test_addition(void)
{
int x;
int y;
while (scanf("%d%d", &x, &y) != EOF)
{
int z = x + y;
printf("%dn", z);
}
}
int main(void)
{
test_addition();
return 0;
}
sources pedro$ ./a.out
3 7
10
3000 7000
10000
3000000 7000000
10000000
3000000000 7000000000
1410065408
2000000000 1
2000000001
2000000000 2000000000
-294967296
3000000000 0
-1294967296
Conclusão: quando uma das parcelas
ou o resultado sai do intervalo dos int,
está tudo estragado.
[-2147483648..2147483647] ou [-231..231-1]
•  Em C, cada número int ocupa uma palavra de
32 bits.
•  A sequência dos valores dos bits corresponde
à representação binária do número.
•  Logo, com 32 bits, podem ser representados
no máximo 232 = 4294967296 números
diferentes.
•  Metade serão negativos, um é o zero e metade
menos um serão positivos.
•  Por isso, o intervalo dos números int é
[-231..231-1], ou [-2147483648..2147483647].
18/12/14 Programação Imperativa 45
Overflow
•  Há overflow de inteiros quando o resultado de
um cálculo com números inteiros cai fora do
intervalos dos números int.
•  Quando há overflow, os cálculos aritméticos
ficam errados, irremediavelmente.
18/12/14 Programação Imperativa 46
sources pedro$ ./a.out
2147483647 1
-2147483648
2147483647 10
-2147483639
2147483647 20
-2147483629
-2147483648 -1
2147483647
Repare, 2147483647 + 1 dá
-2147483648. É como se o sucessor
do maior número fosse o menor
número. Analogamente
-2147483648 – 1 dá 2147483647,
como se o predecessor do menor
número fosse o maior número.
Operações aritméticas, tipo int
•  Adição: x + y
•  Subtração: x – y
•  Multiplicação: x * y
•  Quociente da divisão inteira: x / y
•  Resto da divisão inteira: x % y
18/12/14 Programação Imperativa 47
Cuidados:
•  Não deixar dar overflow.
•  Não deixar o divisor ser zero. Se o divisor for zero,
o programa estoira.
•  Não usar operandos com valor negativo na
operação resto da divisão inteira.
Note bem: ambos os operandos, x
e y, representam expressões cujo
valor é um número int. O resultado,
se houver, é um valor de tipo int.
Testando as operações aritméticas, int
18/12/14 Programação Imperativa 48
void test_operations_int(void)
{
int x;
int y;
while (scanf("%d%d", &x, &y) != EOF)
{
int z1 = x + y;
printf("%dn", z1);
int z2 = x - y;
printf("%dn", z2);
int z3 = x * y;
printf("%dn", z3);
int z4 = x / y;
printf("%dn", z4);
int z5 = x % y;
printf("%dn", z5);
}
}
sources pedro$ ./a.out
20 7
27
13
140
2
6
33 50
83
-17
1650
0
33
14 3
17
11
42
4
2
Operações aritméticas, tipo double
•  Adição: x + y
•  Subtração: x – y
•  Multiplicação: x * y
•  Quociente da divisão: x / y
18/12/14 Programação Imperativa 49
Cuidados:
•  Não deixar o divisor ser zero.
•  Não contar com precisão ilimitada na representação
do resultado.
Note bem: ambos os operandos, x e
y, representam expressões cujo valor
é um número double. O resultado, se
houver, é de tipo double.
Testando as operações aritméticas, double
18/12/14 Programação Imperativa 50
void test_operations_double(void)
{
double x;
double y;
while (scanf("%lf%lf", &x, &y) != EOF)
{
double z1 = x + y;
printf("%fn", z1);
double z2 = x - y;
printf("%fn", z2);
double z3 = x * y;
printf("%fn", z3);
double z4 = x / y;
printf("%fn", z4);
}
}
sources pedro$ ./a.out
25.0 4.0
29.000000
21.000000
100.000000
6.250000
14.0 3.0
17.000000
11.000000
42.000000
4.666667
6.125 0.5
6.625000
5.625000
3.062500
12.250000
0.333333 0.5
0.833333
-0.166667
0.166666
0.666666
Note bem: o operador %, resto
da divisão inteira, não existe com
para números double.
Aritmética mista
•  Quando numa expressão do tipo x+y, x-y, x*y
ou x/y um dos operandos é double e o outro
é int, este é “convertido” automaticamente
para double e aplicam-se as regras da
aritmética de doubles.
18/12/14 Programação Imperativa 51
A conversão inversa, de double para int, é mais
delicada, pois, pode fazer-se de várias maneiras: por
truncagem (isto é, eliminando a parte decimal), para o
inteiro precedente, para o inteiro seguinte, ou para o
inteiro mais próximo. Em cada caso, temos de indicar
qual pretendemos.
Funções matemáticas de biblioteca
•  O C traz um pequeno conjunto de funções
matemáticas, operando sobre doubles:
18/12/14 Programação Imperativa 52
Função Significado
sin(x) Seno de x.
cos(x) Cosseno de x.
tan(x) Tangente de x.
atan2(y, x) Arcotangente de y/x, no intervalo [-π, π].
exp(x) Exponencial de x.
log(x) Logaritmo natural de x.
pow(x, y) x elevado a y.
sqrt(x) Raiz quadrada de x.
floor(x) Maior número inteiro menor ou igual a x.
ceil(x) Menor número inteiro maior ou igual a x.
fabs(x) Valor absoluto de x.
Para usar, fazer
#include <math.h>.
Arredondamento
•  No problema da nota, precisamos de
arredondar a nota exata, para o inteiro mais
próximo, tendo o cuidado de arredondar para
cima as meias unidades.
•  Eis uma função para fazer esse cálculo,
recorrendo à função floor:
•  Note que o resultado é um número inteiro
representado por um double.
18/12/14 Programação Imperativa 53
double round(double x)
{
return floor(x+0.5);
}
Nota final
•  A nota final é o arredondamento da nota
exata e dever ser expressa no tipo int.
•  Devemos pois explicitar a conversão do resul-
tado do arredondamento, de double para int.
•  Observe:
18/12/14 Programação Imperativa 54
int final_grade(double lab, double exam)
{
return (int) round(grade(lab, exam));
}
Em geral, sendo x uma expressão de tipo double, (int) x é
uma expressão de tipo int cujo valor é o valor de x sem a
parte decimal.
Função de teste para a nota exata
•  Acrescentamos o novo cálculo à função
test_grade:
18/12/14 Programação Imperativa 55
void test_grade(void)
{
double lb;
double ex;
while (scanf("%lf%lf", &lb, &ex) != EOF)
{
double v = weighted_average(lb, ex);
printf("%fn", v);
double z = grade(lb, ex);
printf("%fn", z);
int g = final_grade(lb, ex);
printf("%dn", g);
}
} Em geral, é prudente observar também os
resultados intermédios nas funções de teste.
Nota de exame necessária
•  Problema: para passar com y como nota final,
quanto precisa conseguir no exame um aluno
cuja nota da prática é x?
•  Para começar, precisamos de resolver em
ordem a z a inequação 0.3 * x + 0.7 * z >= y.
•  Mas o resultado exato não basta, pois a nota
do exame é expressa com uma casa decimal.
•  E, para mais, se, resolvendo a inequação, z vier
menor que 8.5, isso não serve: o valor de z
tem de ser pelo menos 8.5.
18/12/14 Programação Imperativa 56
Nota necessária exata
•  Calculemos primeiro a nota necessária com a
precisão possível, sem considerar a questão do
8.5.
•  Isso corresponde a resolver a inequação:
18/12/14 Programação Imperativa 57
double exam_exact(double lab, int goal)
{
return (goal - 0.3 * lab) / 0.7;
}
Se o resultado for maior que 20.0, isso
significa que é impossível passar com a
nota desejada.
Arredondamento para cima às décimas
•  Para arredondar para cima, às unidades, temos
a função ceil.
•  Como fazer para arredondar às décimas?
•  Eis o truque: multiplica-se por 10, arredonda-
se às unidades e divide-se por 10:
18/12/14 Programação Imperativa 58
double ceiling_one_decimal(double x)
{
return ceil(x * 10.0) / 10.0;
}
E se quiséssemos arredondar para
cima às milésimas, como faríamos? E
arredondar para cima, às dezenas?
Nota necessária com uma casa decimal
•  Arredonda-se a nota exata, às décimas, para
cima:
18/12/14 Programação Imperativa 59
double exam_one_decimal(double lab, int goal)
{
return ceiling_one_decimal(exam_exact(lab, goal));
}
Temos ainda de considerar o caso em
que esta nota é menor que 8.5, pois
um tal valor não daria para passar.
Funções max e min
•  A função max retorna o valor do maior dos
seus argumentos.
•  A função min, idem, para o menor.
•  Como não existem na biblioteca do C,
programamo-las nós:
18/12/14 Programação Imperativa 60
double max(double x, double y)
{
return x >= y ? x : y;
}
double min(double x, double y)
{
return x <= y ? x : y;
}
Estas duas funções são
muito úteis, muitas vezes.
Nota necessária
•  Se a nota exata arredondada for menor do
que 8.5 a nota necessária é 8.5; caso contrário,
a nota necessária é a nota exata arredondada.
•  Por outras palavras: a nota necessária é o
máximo entre a nota exata arredondada e 8.5:
18/12/14 Programação Imperativa 61
double exam(double lab, int goal)
{
return max(exam_one_decimal(lab, goal), 8.5);
}
sources pedro$ ./a.out
15.0 10
7.857143
7.900000
8.500000
10
8
12.0 15
16.285714
16.300000
16.300000
15
14
12.0 18
20.571429
20.600000
20.600000
18
17
Função de teste
•  Na função de teste, observamos os cálculos inter-
médios e recalculamos a nota final, e também a nota
final se tivéssemos uma décima a menos no exame:
18/12/14 Programação Imperativa 62
void test_exam(void)
{
double lb;
int gl;
while (scanf("%lf%d", &lb, &gl) != EOF)
{
double z1 = exam_exact(lb, gl);
printf("%fn", z1);
double z2 = exam_one_decimal(lb, gl);
printf("%fn", z2);
double z = exam(lb, gl);
printf("%fn", z);
int x1 = grade(lb, z);
printf("%dn", x1);
int x2 = grade(lb, z - 0.1);
printf("%dn", x2);
}
}
Programação Imperativa
Lição n.º 4
Escolhas
Escolhas
•  Instrução if-else.
•  Constantes simbólicas.
•  Declaração de variáveis.
•  If-else em cascata.
18/12/14 Programação Imperativa 64
Preço de uma chamada em roaming
•  Uma chamada feita em roaming num país da
zona 1 para um número em Portugal custa
0,234 euros por minuto.
•  A taxação é feita ao segundo após o primeiro
impulso de 30 segundos.
•  Isto quer dizer que se paga por segundo, mas
paga-se sempre pelo menos 30 segundos.
•  Queremos uma função para dar o preço a
pagar em função da duração da chamada.
•  Os arredondamentos são feitos em cada
chamada, aos milésimos de euro.
18/12/14 Programação Imperativa 65
Preço exato, preço certo
•  A expressão que dá o preço nos primeiros 30
segundos é constante: corresponde a metade
do preço por minuto.
•  A expressão que dá o preço após os primeiros
30 segundos é o produto do número de
segundos pelo preço de cada segundo.
•  O preço de cada segundo é um sexagésimo do
preço do minuto (sem arredondamentos).
•  Usaremos uma função para o preço exato,
calculado com toda a precisão, e uma para o
preço certo, arredondado às milésimas.
18/12/14 Programação Imperativa 66
Constantes simbólicas
•  O preço por minuto é uma constante
arbitrária, atualmente 0.234.
•  Normalmente, evitamos usar “números
mágicos” nos programas.
•  Em vez disso, preferimos constantes
simbólicas:
•  Agora, em vez de 0.234, usaremos, com maior
clareza, PRICE_PER_MINUTE.
18/12/14 Programação Imperativa 67
#define PRICE_PER_MINUTE 0.234
Preço exato, com expressão condicional
•  Em casos simples como este, usamos uma
expressão condicional:
•  Mas, quando queremos dar mais destaque a
cada uma das alternativas, preferimos uma
instrução if-else.
18/12/14 Programação Imperativa 68
double roaming_exact(double x)
{
return x <= 30
? PRICE_PER_MINUTE / 2
: x * PRICE_PER_MINUTE / 60;
}
Preço exato, com instrução if-else
•  Eis a mesma função, programada à base de
uma instrução if-else:
18/12/14 Programação Imperativa 69
double roaming_exact(double x)
{
double result;
if (x <= 30)
result = PRICE_PER_MINUTE / 2;
else
result = x * PRICE_PER_MINUTE / 60;
return result;
} Quando as funções de cálculo são mais do que o
return de uma expressão, usaremos uma variável
result para representar o resultado.Assim, é
habitual as funções terminarem por return result;.
Preço certo, arredondado às milésimas
•  A técnica de arredondamento já é conhecida:
18/12/14 Programação Imperativa 70
double round(double x)
{
return floor(x+0.5);
}
double round_three_decimals(double x)
{
return round(x * 1000) / 1000;
}
double roaming(double x)
{
return round_three_decimals(roaming_exact(x));
}
•  Logo:
Função de teste
•  Na função de teste, é prudente observar
também o cálculo exato:
18/12/14 Programação Imperativa 71
void test_roaming(void)
{
double seconds;
while (scanf("%lf", &seconds) != EOF)
{
double z1 = roaming_exact(seconds);
printf("%fn", z1);
double z2 = roaming(seconds);
printf("%fn", z2);
}
}
$ ./a.out
30
0.117000
0.117000
15
0.117000
0.117000
31
0.120900
0.121000
35
0.136500
0.137000
100
0.390000
0.390000
103
0.401700
0.402000
Declaração de variáveis
•  A declaração da variável indica explicitamente o tipo
de valores que a variável pode representar.
•  Determina também, implicitamente, a “quantidade”
de memória necessária para guardar o valor da
variável.
•  Cada variável tem de ser declarada antes de ser
usada.
•  Normalmente, declara-se a variável mesmo antes da
primeira utilização.
•  Se possível, declara-se e inicializa-se no mesmo passo.
•  Quase sempre, as variáveis não variam: recebem um
valor (por leitura, por cálculo) e guardam esse valor
até ao fim.
18/12/14 Programação Imperativa 72
Estacionamento no aeroporto de Faro
•  Nos parques P1 e P2 do aeroporto de Faro, o
preço do estacionamento é dado pela seguinte
tabela:
18/12/14 Programação Imperativa 73
Unidades de taxação Preço
Primeira unidade de 15 minutos 0.60
Restantes unidades de 15 minutos 0.60
Máximo para o primeiro dia 9.00
Preço hora após 24 horas 1.50
Máximo segundo dia e seguintes 9.00
Análise
•  Na verdade, há dois casos apenas: o primeiro
dia e os restantes dias.
•  No primeiro dia paga-se por 60 cêntimos por
quarto de hora, mas paga-se no máximo 9
euros.
•  No outros dias paga-se 1.50 euros por hora,
mas paga-se no máximo 9 euros.
•  Como as expressões dos cálculos são
complicadas, vamos usar if-else.
18/12/14 Programação Imperativa 74
Função parking
•  Atenção às contas:
18/12/14 Programação Imperativa 75
double parking(double minutes)
{
double result;
double days_complete = floor(minutes / 1440);
if (minutes <= 1440)
result = min(9.0, ceil(minutes / 15) * 0.6);
else
result =
9.0 * days_complete +
min(9.0,
ceil((minutes - days_complete * 1440) / 60) * 1.5);
return result;
}
Um dia tem 1440
minutos!
Neste caso, por natureza, não é preciso
arredondar os resultado, pois nunca terão mais do
que duas casas decimais.
Em rigor, a variável days_complete
só é necessária no ramo else.
Calculamos logo à cabeça, apenas
para aligeirar o código.
Reanálise
•  Podemos admitir que as regras para calcular o
preço do estacionamento não mudam, durante
algum tempo, mas que os valores na tabela
podem mudar com alguma frequência.
•  Por exemplo, o custo dos primeiros 15
minutos pode baixar para 40 cêntimos, para
incentivar estadias muito curtas.
•  Ou o máximo nos outros dias (depois do
primeiro) pode subir para 12 euros, para
castigar estadia longas.
•  Para incorporar estas mudanças, não bastaria
substituir as constantes.
18/12/14 Programação Imperativa 76
Nova estratégia
•  Programemos “em função” dos valores da
tabela, os quais serão representados por
constantes simbólicas:
•  Vendo bem, agora há três casos: os primeiros
15 minutos; o resto do primeiro dia; os dias
seguintes.
•  Usaremos instruções if-else em cascata.
18/12/14 Programação Imperativa 77
#define UNIT_1 0.60
#define UNITS_OTHER 0.60
#define MAX_DAY_1 9.00
#define PRICE_PER_HOUR 1.50
#define MAX_DAY_OTHERS 9.00
Haver aqui repetições de
valores deve ser uma
coincidência passageira.
Função parking, melhor e mais complicada
•  Veja com atenção:
18/12/14 Programação Imperativa 78
double parking(double x)
{
double result;
double days_complete = floor(x / 1440);
if (x <= 15)
result = UNIT_1;
else if (x <= 1440)
result = min(MAX_DAY_1,
UNIT_1 + ceil((x - 15) / 15) * UNITS_OTHER);
else
result = MAX_DAY_1 +
MAX_DAY_OTHERS * (days_complete - 1) +
min(MAX_DAY_OTHERS,
ceil((x - days_complete * 1440) / 60) *
PRICE_PER_HOUR);
return result;
}
É mais complicada porque a
vida é complicada.
Programação Imperativa
Lição n.º 5
Ciclos
Ciclos
•  Ciclos for.
•  Representação dos números double em
memória.
•  Afetação.
•  Operadores de afetação.
•  Formatação de números double.
18/12/14 Programação Imperativa 80
Soma geométrica
•  Queremos calcular a soma S(x, n) = 1 + x + x2
+ ... + xn-1, para um dado x e um dado n.
•  Existe uma fórmula: S(x, n) = (xn-1)/(x-1).
•  Para x=2, esta fórmula dá 1+2+4+...+2n-1 =
2n-1.
•  Para x=1/2, dá 1+1/2+1/4+... = 2.
•  Estes dois resultados têm muito interesse
computacional. Lembre-se deles!
•  Por outro lado:
S(x, n) = n == 0 ? 0 : 1 + x * S(x, n-1)
18/12/14 Programação Imperativa 81
•  Calculemos à mão S(3, 5):
Calculando recursivamente
S(3, 5) = 1 + 3 * S(3, 4)
= 1 + 3 * (1 + 3 * S(3, 3))
= 1 + 3 * (1 + 3 * (1 + 3 * S(3, 2)))
= 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * S(3, 1))))
= 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * S(3, 0)))))
= 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * 0))))
= 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * 1))))
= 1 + 3 * (1 + 3 * (1 + 3 * 4)))
= 1 + 3 * (1 + 3 * 13))
= 1 + 3 * 40
= 121
18/12/14 Programação Imperativa 82
Curioso: calculamos uma soma
de potências com expoentes
sucessivos, mas não usámos a
potenciação.
Função para a soma das potências
•  Exprimimos em C a definição da função S:
18/12/14 Programação Imperativa 83
double sum_geometric(double x, int n)
{
return n == 0
? 0.0
: 1.0 + x * sum_geometric(x, n-1);
}
Formatos para double no printf
•  O especificador “%f” indica que o número
aparecerá com parte inteira e parte decimal.
Por defeito, a parte decimal vem arredondada
com 6 algarismos.
•  O especificador “%e” indica que o número
aparecerá em notação exponencial, por
exemplo, 4.095000e+03. A parte inteira vem
está entre1 e 9 e, por defeito, a parte decimal
vem arredondada com 6 algarismos.
•  O especificador “%g” indica que o número virá
na forma mais “apropriada”.
18/12/14 Programação Imperativa 84
Testando a soma das potências
•  Calculamos e mostramos o resultado usando
os três formatos:
18/12/14 Programação Imperativa 85
void test_sum_geometric(void)
{
double x;
int n;
while (scanf("%lf%d", &x, &n) != EOF)
{
double z = sum_geometric(x, n);
printf("%fn", z);
printf("%en", z);
printf("%gn", z);
}
}
$ ./a.out
2 16
65535.000000
6.553500e+04
65535
10 12
111111111111.000000
1.111111e+11
1.11111e+11
0.5 10
1.998047
1.998047e+00
1.99805
0.1 8
1.111111
1.111111e+00
1.11111
Fixando a precisão
•  Para escolher o número de casas decimais no
caso do “%f” e do “%e” ou o número de
algarismos usados no “%g”, fixamos a precisão
da escrita.
•  Por exemplo, mudemos a precisão de 6 (valor
por defeito) para 20, nos 3 printf:
18/12/14 Programação Imperativa 86
printf("%.20fn", z);
printf("%.20en", z);
printf("%.20gn", z);
Experimentando com precisão excessiva
•  Observe, com precisão 20:
18/12/14 Programação Imperativa 87
$ ./a.out
2 15
32767.00000000000000000000
3.27670000000000000000e+04
32767
10 14
11111111111111.00000000000000000000
1.11111111111110000000e+13
11111111111111
2 40
1099511627775.00000000000000000000
1.09951162777500000000e+12
1099511627775
0.1 15
1.11111111111111005023
1.11111111111111005023e+00
1.1111111111111100502
0.1 30
1.11111111111111116045
1.11111111111111116045e+00
1.1111111111111111605
O último teste mostra que o
tipo double não consegue
representar números com
mais que 17 algarismos.
Iteração
•  Recorde a definição da função S:
S(x, n) = n == 0 ? 0 : 1 + x * S(x, n-1)
•  Quer dizer: se R for o valor de S(x, n) então 1+x * R
é o valor de S(x, n+1).
•  Portanto, começando com R = 0 e fazendo
R = 1 + x * R, repetidamente, n vezes, chega-se ao
valor de S(x, n):
18/12/14 Programação Imperativa 88
R = 0
R = 1 + x * R // x0
R = 1 + x * R // x0 + x1
R = 1 + x * R // x0 + x1 + x2
...
R = 1 + x * R // x0 + x1 + x2 + ... xn-1
Soma das potências, versão iterativa
•  Para repetir uma instrução um certo número
de vezes, usa-se um ciclo for:
18/12/14 Programação Imperativa 89
double sum_geometric_i(double x, int n)
{
double result = 0.0;
for (int i = 0; i < n; i++)
result = 1.0 + x * result;
return result;
}
No fim do passo i, a variável result contém o valor
1+x+x2+...+xi. Portanto, no fim do ciclo for, conterá
1+x+x2+...xn-1, que constitui a soma pretendida.
Com este ciclo for,
repete-se n vezes.
double sum_geometric_t(double x, int n)
{
double result = 0.0;
double term = 1.0;
for (int i = 0; i < n; i++)
{
result += term;
term *= x;
}
return result;
}
Cada novo resultado parcial é
obtido somando o resultado
parcial corrente ao termo
corrente; cada novo termo é
obtido multiplicando o termo
corrente por x.
Versão iterativa, variante
•  Consegue-se o mesmo efeito, enumerando os
termos da lista das potências, acumulando a
soma no resultado:
18/12/14 Programação Imperativa 90
Afetação
•  Que significa, em programação x = y?
•  A expressão x = y é uma expressão de afetação.
•  Em geral, x e y são expressões.
•  A expressão x, à esquerda, representa uma variável,
isto é, uma posição de memória.
•  A expressão y, à direita, representa um valor, que
deve pertencer a um tipo que pode ser armazenado
na posição de memória representada por x.
•  O efeito de x = y é armazenar na posição
representada por x o valor representado por y.
•  Em geral, o valor de uma variável é o valor que mais
recentemente foi armazenado na posição de
memória representada por essa variável.
18/12/14 Programação Imperativa 91
Operadores de afetação
•  O significado dos operadores de afetação +=,
−=, *=, /= e %= é “intuitivo”:
18/12/14 Programação Imperativa 92
Utilização Significado
x += y x = x + y
x −= y x = x − y
x *= y x = x * y
x /= y x = x / y
x %= y x = x % y
Variante overkill
•  Calcular cada termo da sucessão das
potências, usando pow(x, i) seria overkill:
18/12/14 Programação Imperativa 93
double sum_geometric_bad(double x, int n)
{
double result = 0.0;
for (int i = 0; i < n; i++)
result += pow(x, i);
return result;
} Calcular xi por meio de pow(x, i) é
esbanjamento de recursos, pois o
valor de xi-1 terá sido calculado no
passo anterior e para calcular xi,
basta multiplicar esse valor por x.
void test_sum_geometric_all(void)
{
double x;
int n;
while (scanf("%lf%d", &x, &n) != EOF)
{
double z1 = sum_geometric(x, n);
printf("%.16gn", z1);
double z2 = sum_geometric_i(x, n);
printf("%.16gn", z2);
double z3 = sum_geometric_t(x, n);
printf("%.16gn", z3);
double z4 = sum_geometric_bad(x, n);
printf("%.16gn", z4);
}
}
$ ./a.out
2 20
1048575
1048575
1048575
1048575
10 12
111111111111
111111111111
111111111111
111111111111
2 60
1.152921504606847e+18
1.152921504606847e+18
1.152921504606847e+18
1.152921504606847e+18
0.5 30
1.999999998137355
1.999999998137355
1.999999998137355
1.999999998137355
Função de teste
•  Eis uma função de teste para as quatro
variantes da soma de potências:
18/12/14 Programação Imperativa 94
Soma de quadrados
•  Baseemo-nos na variante overkill da soma
geométrica para programar a soma de
quadrados:
18/12/14 Programação Imperativa 95
double sum_squares(int n)
{
double result = 0.0;
for (int i = 0; i < n; i++)
result += i * i;
return result;
}
Há uma expressão polinomial que calcula a soma dos quadrados dos
n primeiros números naturais (0+1+4+9+...+(n-1)2), pelo que
calcular iterativamente é apenas um exercício de programação.A
expressão é (n-1)*n*(2*n-1)/6.
Soma de quadrados, variante elementar
•  É claro que (n+1)2 – n2 = 2*n+1.
•  Logo, podemos calcular (n+1)2 a partir de n2,
somando 2*n+1:
18/12/14 Programação Imperativa 96
double sum_squares_e(int n)
{
double result = 0.0;
double term = 0.0;
for (int i = 0; i < n; i++)
{
result += term;
term += 2 * i + 1;
}
return result;
}
Soma de quadrados, mais elementar ainda
•  Usando a mesma técnica, sabemos que
(2(n+1) + 1) – (2*n+1) = 2.
•  Logo, a sequência das diferenças entre quadrados
cresce de 2 em 2:
18/12/14 Programação Imperativa 97
double sum_squares_f(int n)
{
double result = 0.0;
double term = 0.0;
double delta = 1.0;
for (int i = 0; i < n; i++)
{
result += term;
term += delta;
delta += 2.0;
}
return result;
}
Este é um exercício de programação
clássico: calcular a soma dos
quadrados nos n primeiros números
naturais usando apenas adições.
Soma de quadrados, polinomial
•  Eis a função que implementa o cálculo direto
da soma dos quadrados dos n primeiros
números naturais:
18/12/14 Programação Imperativa 98
double sum_squares_p(int n)
{
return (n - 1.0) * n * (2.0*n - 1.0)/6;
}
Questão técnica: se tivéssemos programado só com inteiros int,
assim:
return (n-1) * n * (2*n - 1) / 6;
o computador usaria aritmética de inteiros (antes de converter o
resultado para double, no fim) e daria overflow logo que o produto
no numerador ultrapassasse 231-1, o que acontece para n = 1025.
Programação Imperativa
Lição n.º 6
Ciclos for e ciclos while
Ciclos for e ciclos while
•  Utilização dos ciclos for.
•  Utilização dos ciclos while.
•  Exemplos: soma de sequências, fatorial,
função 3x+1, comprimento de
números.
18/12/14 Programação Imperativa 100
Utilização dos ciclos for
•  Usamos um ciclo for para repetir a execução
de uma instrução nas situações em sabemos à
partida quantas vezes é preciso repetir.
•  Tipicamente, número de repetições ou é fixo
(o que é raro) ou vem numa variável.
•  No seio da instrução repetida, podemos usar a
variável de controlo que enumera as repetições.
•  Frequentemente, a enumeração começa em 0
ou em 1 e vai de 1em 1.
•  A instrução repetida pode ser uma instrução
composta, agrupando várias instruções.
18/12/14 Programação Imperativa 101
Exemplo: progressão aritmética
18/12/14 Programação Imperativa 102
•  Eis uma função para calcular iterativamente a
soma dos n primeiros termos de uma
progressão geométrica cujo primeiro termo é
x e cuja razão é r:
double sum_progression(double x, double r, int n)
{
double result = 0.0;
for (int i = 0; i < n; i++)
{
result += x;
x += r;
}
return result;
}
Já sabemos que há uma fórmula fechada para
isto, pelo que esta função deve ser considerada
apenas um exercício de programação.
Note bem: sabemos que
queremos somar n termos.
Logo, usamos ciclo for.
Exemplo: fatorial
18/12/14 Programação Imperativa 103
•  O fatorial de um número inteiro n é o
produto de todos os números inteiros entre 1
e n (inclusive):
double factorial(int n)
{
double result = 1.0;
for (int i = 1; i <= n; i++) // <=
result *= i;
return result;
}
Note bem: aqui a variável de
controlo varia entre 1 e n
(inclusive); no exemplo da
página anterior, variava entre 0
e n-1 (inclusive).
Queremos multiplicar n
números. Logo, usamos
ciclo for.
Note bem: a variável de
controlo é usada na
instrução repetida.
Testando o fatorial
18/12/14 Programação Imperativa 104
•  Eis uma função de teste:
void test_factorial(void)
{
int x;
while (scanf("%d", &x) != EOF)
{
double z = factorial(x);
printf("%.15gn", z);
}
}
$ ./a.out
1
1
5
120
10
3628800
20
2.43290200817664e+18
30
3.04140932017134e+64
100
9.33262154439441e+157
150
5.71338395644585e+262
170
7.25741561530799e+306
171
inf
300
inf
O maior número double é
aproximadamente 1.796931e308. Mais
que isso, é infinito, representado em
C por inf.
Problema: soma 1 + 1/2+ 1/4 + 1/8 + ...
18/12/14 Programação Imperativa 105
•  A soma dos inversos das potências de 2 tende
para 2.
•  Quantos termos é preciso somar para que o
resultado seja maior que 2 – x, para um x
dado?
•  Neste caso, não sabemos à partida quantas
vezes temos de repetir a adição r = 1 + r/2
(cf. função sum_geometric, lição 5.)
•  (Se soubéssemos, já tínhamos a solução do
problema...)
Ciclos while
•  O ciclo while é usado para repetir uma
instrução um número indeterminado de vezes.
•  As repetições terminam logo que uma certa
condição deixa de se verificar.
•  A condição é representada por uma expressão
lógica.
•  Isto é, as repetições terminam logo que o
valor da expressão booleana seja 0,
representando falso.
•  Não haverá repetições se a expressão valer 0
logo de início.
18/12/14 Programação Imperativa 106
Contanto termos da soma aproximada
•  Repetimos a instrução r = 1 + r/2, enquanto o valor de
r, que representa a soma parcial, for menor que 2 – x,
sendo x o tal valor dado.
•  Em paralelo, incrementamos um contador de termos
adicionados:
18/12/14 Programação Imperativa 107
int count_terms(double x)
{
int result = 0;
double sum = 0.0;
while (sum <= 2 - x)
{
sum = 1 + sum / 2;
result++;
}
return result;
}
O contador é
o resultado da
função.
Testando a contagem de termos
•  Contamos os termos e depois verificamos que
a contagem confere, usando a função
sum_geometric:
18/12/14 Programação Imperativa 108
void test_count_terms(void)
{
double x;
while (scanf("%lf", &x) != EOF)
{
int z1 = count_terms(x);
printf("%dn", z1);
double z2 = sum_geometric(0.5, z1);
printf("%.15fn", z2);
}
}
$ ./a.out
0.1
5
1.937500000000000
0.01
8
1.992187500000000
0.001
11
1.999023437500000
0.000001
21
1.999999046325684
Problema 3x+1
•  Seja a seguinte função f:
•  Qualquer que seja x, a sequência x, f(x), f(f(x)),
f(f(f(x))), ..., eventualmente alcança o valor 1,
após o que oscila 1, 4, 2, 1, 2, 4, 1, ...,
indefinidamente.
•  Qual é o comprimento da sequência que
começa em x e termina no primeiro 1?
18/12/14 Programação Imperativa 109
f (x) =
3x +1, se x for ímpar
x / 2, se não
!
"
#
http://en.wikipedia.org/wiki/Collatz_conjecture
http://uva.onlinejudge.org/external/1/100.html
Preliminares
•  Primeiro, a função f:
•  Repare na expressão x % 2 == 1.
•  Significa “ser o resto da divisão de x por 2
igual a 1”.
•  Por outras palavras,“ser x um número ímpar”.
18/12/14 Programação Imperativa 110
int f(int x)
{
return x % 2 == 1 ? 3 * x + 1 : x / 2;
}
Observando a sequência
•  Para perceber melhor de que se trata, programemos
uma função para mostrar a sequência que começa
em x e chega até 1, chamada “ciclo” de x:
18/12/14 Programação Imperativa 111
void show_cycle(int x)
{
while (x != 1)
{
printf("%d ", x);
x = f(x);
}
printf("%dn", x);
}
void test_cycle(void)
{
int x;
while (scanf("%d", &x) != EOF)
show_cycle(x);
}
$ ./a.out
3
3 10 5 16 8 4 2 1
9
9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
512
512 256 128 64 32 16 8 4 2 1
Comprimento do ciclo
•  É parecida com show_cycle, substituindo o
printf pelo incremento do contador:
18/12/14 Programação Imperativa 112
int cycle_length(int x)
{
int result = 0;
while (x != 1)
{
result++;
x = f(x);
}
result++;
return result;
}
Refinando a função de teste
•  Acrescentamos à função de teste uma
chamada da nova função:
18/12/14 Programação Imperativa 113
void test_cycle(void)
{
int x;
while (scanf("%d", &x) != EOF)
{
show_cycle(x);
int z = cycle_length(x);
printf("%dn", z);
}
} $ ./a.out
11
11 34 17 52 26 13 40 20 10 5 16 8 4 2 1
15
40
40 20 10 5 16 8 4 2 1
9
Comprimento de um número
18/12/14 Programação Imperativa 114
•  O comprimento de um número inteiro é o
número de algarismos da sua representação
decimal.
•  Exprime-se recursivamente, em termos do
comprimento da décima parte do número.
•  Mas quando é menor ou igual a 9, o
comprimento é 1:
int decimal_length(int x)
{
return x <= 9 ? 1 : 1 + decimal_length(x / 10);
}
Variante iterativa
18/12/14 Programação Imperativa 115
•  Dividir por 10, sucessivamente, enquanto for
maior que 9, e contar as divisões:
int decimal_length_i(int x)
{
int result = 1;
while (x > 9)
{
result++;
x /= 10;
}
return result;
}
Testando o comprimento do número
18/12/14 Programação Imperativa 116
void test_decimal_length(void)
{
int x;
while (scanf("%d", &x) != EOF)
{
int z1 = decimal_length(x);
printf("%dn", z1);
int z2 = decimal_length_i(x);
printf("%dn", z2);
}
}
$ ./a.out
6413
4
4
9761826
7
7
5
1
1
0
1
1
3321
4
4
•  Testamos as duas variantes:
Programação Imperativa
Lição n.º 7
Arrays
Arrays
•  Generalidades sobre arrays.
•  Funções sobre arrays.
18/12/14 Programação Imperativa 118
Arrays para quê?
•  Até agora, todos os programa que vimos
calculam a partir de uns poucos números,
desses cálculos resultando um outro número.
•  Frequentemente teremos, não uns poucos
números, mas sim milhentos, e queremos a
partir de eles calcular outros milhentos.
•  Mais adiante teremos não só milhentos
números, mas também milhentas palavras e,
mais geralmente, milhentos objetos de
diversos tipos.
•  Usaremos arrays para representar nos
programas milhentos objetos do mesmo tipo.18/12/14 Programação Imperativa 119
Arrays
•  Os arrays são sequências de objetos, todos do
mesmo tipo, acessíveis para inspeção e para
modificação por meio dos respetivos índices.
•  Os índices são números inteiros.
•  O índice do primeiro elemento é zero; o
índice do segundo elemento é 1; o índice do
terceiro elemento é 2 e assim por diante.
•  Se o array tiver N elementos, o índice do
último elemento é N-1.
18/12/14 Programação Imperativa 120
Daqui vem a “tradição” de, em programação,
enumerar os objetos a partir de zero.
Cultura geral: a palavra “array”
•  Em inglês não técnico, o substantivo array é usado
para significar um conjunto considerável de objetos
de um certo tipo, dispostos de maneira regular: “an
array of troops in battle order”; “there is a vast array of
literature on the topic”; “a bewildering array of choices”.
•  A palavra “array” vem do latim “ad-redare”, um verbo
que significa “arranjar”,“colocar em ordem”.
•  De “ad-redare” derivam as palavras portuguesas
“arriar”,“arrear”,“arreio” (mas não “arredar”).
18/12/14 Programação Imperativa 121
Fontes:
WordWeb Dictionary © WordWebSoftware.com.
http://www.etymonline.com/index.php?term=array.
Dictionary, v 2.2.1© Apple Inc.
http://www.ciberduvidas.com/pergunta.php?id=25368.
http://pt.wiktionary.org/wiki/arredar#Etimologia.
Perante isto, e não só,
talvez pudéssemos usar
“arreios”, em português...
Arrays em memória
•  Já sabemos que um objeto de tipo int ocupa
em memória uma palavra de 4 bytes.
•  Também sabemos que um objeto de tipo
double ocupa duas palavras de 4 bytes, isto é,
8 bytes ao todo.
•  Um array de int com capacidade para N
objetos ocupará 4 * N bytes.
•  Esses 4 * N bytes ficam todos de seguida na
memória do computador.
•  Analogamente para os arrays de double: neste
caso serão 8 * N bytes, todos de seguida.
18/12/14 Programação Imperativa 122
Capacidade de um array
•  A capacidade de um array determina o
número de objetos que o array pode conter,
em cada momento.
•  A capacidade é fixa: uma vez estabelecida,
quando o array é criado, não mais mudará.
•  Em geral, se um objeto de tipo T ocupa Z
bytes, um array de elementos do tipo T com
capacidade C ocupará um bloco de memória
com C * Z bytes.
•  Esse bloco de memória é inamovível.
18/12/14 Programação Imperativa 123
Capacidade e tamanho
•  Muitas vezes, os arrays são dimensionados por
excesso, para poder acondicionar todos os
conjuntos de valores que possam surgir.
•  Por isso, frequentemente, o número de objetos
presente no array é menor que a capacidade.
•  O número de elementos presentes no array, em
cada momento, é o tamanho do array.
•  Se a capacidade for C e o tamanho for N, as
C – N posições de maior índice ficam
desaproveitadas.
•  Durante os cálculos, o tamanho pode mudar; a
capacidade não!
18/12/14 Programação Imperativa 124
Propriedade fundamental dos arrays
Num array, o custo de
aceder ao primeiro
elemento é igual a custo
de aceder ao último ou a
qualquer outro elemento.
18/12/14 Programação Imperativa 125
Limites dos índices
•  Tentar aceder a um array fora dos limites dos
índices, isto é, usando um índice menor que
zero ou maior ou igual ao tamanho, é um
grave erro de programação.
•  Excetua-se o caso de querermos acrescentar
um elemento ao array.
•  Para acrescentar, acedemos ao índice dado
pelo valor do tamanho, o qual indica a
primeira posição livre, e colocamos nesta
posição o valor que queremos acrescentar.
•  Logo a seguir, incrementamos o valor do
tamanho.18/12/14 Programação Imperativa 126
O qual, não obstante,
cometemos muitas vezes.
Problema: array dos dígitos.
18/12/14 Programação Imperativa 127
•  Dado um número inteiro não negativo x,
queremos programar uma função para
construir um array que contenha os dígitos de
x, isto é, os números que correspondem aos
algarismos usados na representação decimal
de x.
•  Por exemplo, se o número for 2015, o array
ficará com os elementos <5, 1, 0, 2> e o
tamanho será 4.
Repare: 5, que é o dígito menos significativo,
fica na posição de índice 0; 1 fica na posição
de índice 1; 0 fica na posição de índice 2; e 2
fica na posição de índice 3.
Função digits
18/12/14 Programação Imperativa 128
•  Os argumentos da função serão o número
cujos dígitos queremos e o array onde vamos
guardar os dígitos calculados.
•  A função retorna o tamanho do array, no final
das operações.
•  Observe a declaração da função, ainda vazia:
int digits(int x, int *a)
{
...
}
Repare: int * é o tipo
dos arrays de int.
Aquecimento: contando os dígitos
18/12/14 Programação Imperativa 129
•  Contar os dígitos de números naturais é mais
difícil do que contar os dígitos de números
inteiros positivos, porque o zero introduz uma
irregularidade.
•  De facto, se x estiver no intervalo [10n..10n+1[,
x tem n+1 dígitos.
•  Ora o zero escapa a estes intervalos e tem de
ser tratado à parte.
•  Por isso, comecemos pelo caso mais
conveniente: contar os dígitos de um número
inteiro positivo.
Função count_digits_positive
18/12/14 Programação Imperativa 130
•  É uma variante mais simples da função
decimal_length_i da lição anterior:
int count_digits_positive(int x)
{
int result = 0;
while (x > 0)
{
result++;
x /= 10;
}
return result;
}
Note bem: o argumento
deve ser positivo. Se for
zero (ou negativo) o
resultado é inválido.
Função count_digits
18/12/14 Programação Imperativa 131
•  Consideramos o caso particular de o
argumento ser zero, em que o resultado é 1.
•  Fora isso, usamos o caso geral, por meio da
função anterior.
•  Observe:
int count_digits(int x)
{
int result = 1;
if (x > 0)
result = count_digits_positive(x);
return result;
}
Aprecie o estilo: evitamos
um if-else, inicializando o
resultado com o valor por
defeito, por assim dizer.
Função digits_positive
18/12/14 Programação Imperativa 132
•  Evitemos as chatices do zero, baseando-nos na
função count_digits_positive.
•  Agora, além de contar, acrescentamos cada
dígito ao array:
int digits_positive(int x, int *a)
{
int result = 0;
while (x > 0)
{
a[result++] = x % 10;
x /= 10;
}
return result;
}
Repare: o resultado
representa o tamanho do
array, depois da operação.
Função digits
18/12/14 Programação Imperativa 133
•  Esta constitui o caso geral:
int digits(int x, int *a)
{
int result = 1;
a[0] = 0;
if (x > 0)
result = digits_positive(x, a);
return result;
}
Deixamos as funções de
teste para mais tarde...
Outro exemplo: inverter o array
18/12/14 Programação Imperativa 134
•  Queremos construir um array com os
elementos de outro array, por ordem inversa.
•  Observe:
•  O array b é o array de saída; o array a é o
array de entrada.
•  A função devolve o tamanho do array de saída.
int mirror(const int *a, int n, int *b)
{
for (int i = 0; i < n; i++)
b[n-1-i] = a[i];
return n;
}
O qualificador const indica que o array a
não será modificado na função.
Programação Imperativa
Lição n.º 8
Arrays e memória
Arrays e memória
•  Lendo e escrevendo arrays.
•  Observando os arrays na memória.
•  Buffer overflow.
18/12/14 Programação Imperativa 136
Escrevendo arrays
•  Ocasionalmente queremos observar na
consola o conteúdo dos nossos arrays.
•  Eis uma função simples que escreve os valores
de todos os elementos na mesma linha, cada
um antecedido por um espaço, mudando de
linha no final:
18/12/14 Programação Imperativa 137
void ints_println_basic(const int *a, int n)
{
for (int i = 0; i < n; i++)
printf(" %d", a[i]);
printf("n");
}
Testando a função digits
•  Usemos a função ints_println_basic para
testar na consola a função digits:
18/12/14 Programação Imperativa 138
void test_digits(void)
{
int x;
while (scanf("%d", &x) != EOF)
{
int a[10];
int n = digits(x, a);
ints_println_basic(a, n);
}
}
$ ./a.out
2015
5 1 0 2
300
0 0 3
7
7
2147483647
7 4 6 3 8 4 7 4 1 2
0
0
Parece que o array está ao contrário, mas
não está: o primeiro elemento, de índice 0,
corresponde ao algarismo das unidades, etc.
Declaração de arrays
•  Repare bem: ao declarar um array, indicamos a
sua capacidade:
•  A capacidade determina a quantidade de
memória reservada para o array.
•  Quando usamos um array como argumento
de uma função, não indicamos a capacidade,
mas tipicamente passamos em argumento
também o tamanho do array, nos arrays de
entrada, ou devolvemos o tamanho calculado,
nos arrays de saída.
18/12/14 Programação Imperativa 139
int a[10];
Preciosismo na função ints_println_basic
•  Aquele espaço no início da linha, antes do
primeiro valor, é deveras irritante.
•  De facto, o que queremos é separar cada dois
valores consecutivos por um espaço e não
colocar um espaço antes de cada valor.
•  Ou seja, queremos um espaço antes de cada
valor, exceto antes do primeiro.
•  Logo, o primeiro elemento do array tem de
ser tratado à parte.
18/12/14 Programação Imperativa 140
2015
5 1 0 2
Função de escrita, básica
•  Note que se o array estiver vazio, isto é, se o
tamanho for zero, a função apenas muda de
linha.
18/12/14 Programação Imperativa 141
void ints_println_basic(const int *a, int n)
{
if (n > 0)
{
printf("%d", a[0]);
for (int i = 1; i < n; i++) // i = 1
printf(" %d", a[i]);
}
printf("n");
}
Testando de novo
•  Fazemos como antes, mas em cada caso
invertemos o array, com a função mirror, para
experimentar:
18/12/14 Programação Imperativa 142
void test_mirror(void)
{
int x;
while (scanf("%d", &x) != EOF)
{
int a[10];
int n = digits(x, a);
ints_println_basic(a, n);
int b[10];
int m = mirror(a, n, b);
ints_println_basic(b, m);
}
}
$ ./a.out
1945
5 4 9 1
1 9 4 5
20141016
6 1 0 1 4 1 0 2
2 0 1 4 1 0 1 6
5
5
5
Lendo arrays
•  Por hipótese, queremos ler da consola uma
sequência de números, até ao fim dos dados.
•  Cada número lido é acrescentado ao array:
18/12/14 Programação Imperativa 143
int ints_get(int *a)
{
int result = 0;
int x;
while (scanf("%d", &x) != EOF)
a[result++] = x;
return result;
}
Testando a leitura de arrays
•  Lemos um array, invertemo-lo para outro, com a
função mirror, e mostramos o array lido e o array
invertido:
18/12/14 Programação Imperativa 144
void test_ints_get()
{
int a[1000];
int n = ints_get(a);
int b[1000];
int m = mirror(a, n, b);
ints_println_basic(a, n);
ints_println_basic(b, m);
}
O valor 1000 para a capacidade é um
valor arbitrário, apenas para teste.
$ ./a.out
1 2 3 4 5
1 2 3 4 5
5 4 3 2 1
$ ./a.out
12 34 45 67 89 97 86 75 64 53 42 31 54 63 72 81 90
12 34 45 67 89 97 86 75 64 53 42 31 54 63 72 81 90
90 81 72 63 54 31 42 53 64 75 86 97 89 67 45 34 12
Repare que esta função de teste não é iterativa.
A iteração existente está na leitura dos dados.
Buffer overflow
•  Se lermos números demais, ultrapassando a
capacidade do array, causamos buffer overflow.
•  A memória fica corrompida e, a partir daí, o
programa está comprometido.
•  Experimentemos com uma função de teste simples:
18/12/14 Programação Imperativa 145
void test_buffer_overflow_1()
{
int a[4];
int n = ints_get(a);
ints_println_basic(a, n);
}
$ ./a.out
1 3 5 7
1 3 5 7
OK
$ ./a.out
1 3 5 7 9 11 13 15
1 3 5 7 9 11 13 15
Abort trap: 6
$
int main(void)
{
test_buffer_overflow_1();
printf("OKn");
return 0;
}
No segundo teste, em que há buffer
overflow, o programa estoira à saída da
função de teste, antes de executar a
instrução printf na função main.
Observando a memória do programa
•  Usando oVisual Studio em Windows ou o Xcode em
MacOS, podemos parar o programa onde quisermos
e observar a memória.
•  Aqui, parámos antes da leitura:
18/12/14 Programação Imperativa 146
A azul, a memória da variável n;
a verde, a memória do array a.
Nesta altura, a memória
contém “lixo”.
Preenchimento da memória
•  Logo a seguir à leitura, os valores de n e as
posições lidas do array a ficam preenchidas:
18/12/14 Programação Imperativa 147
O conteúdo da memória está representado
em notação hexadecimal, da direita para a
esquerda: 04 00 00 00 é, na verdade, 00 00
00 04, ou seja 4, em notação decimal. Isto é a consola, no Xcode: os
valores lidos foram 1, 3, 5 e 7.
Exemplo com dois arrays
•  Consideremos a seguinte função de teste, com dois
arrays:
18/12/14 Programação Imperativa 148
void test_buffer_overflow_2()
{
int a[10];
int n = ints_get(a);
int b[4];
int m = mirror(a, n, b);
ints_println_basic(a, n);
ints_println_basic(b, m);
}
$ ./a.out
10 20 30
10 20 30
30 20 10
$ ./a.out
5 10 15 20
5 10 15 20
20 15 10 5
$ ./a.out
7 14 21 28 35 42 49 56
7 14 14 7 35 42 49 56
56 49 42 35 7 14 14 7
No terceiro teste, o array b
transbordou e corrompeu o array
a. Mas note que o programa
terminou normalmente.
Corrupção da memória (1)
•  Eis o estado da memória, após a leitura de 8
números: 7, 14, 21, 28, 35, 42, 49, 56:
18/12/14 Programação Imperativa 149
A azul, m, ainda com “lixo”; a cor
de rosa, n, com valor 8; a verde, b,
ainda não inicializado; a amarelo,
a, com 8 posições preenchidas.
Corrupção da memória (2)
•  Eis o estado da memória, após a chamada da função
mirror e das duas escritas:
18/12/14 Programação Imperativa 150
Recapitulemos as operações da função mirror.
Primeiro b[7] = a[0].Mas b[7] coincide com a[3]. Logo
a[3] fica com 7. Depois b[6] = a[1]. Mas b[6] coincide
com a[2]. Logo a[2] fica com 14. Quer dizer, por via
destas duas operações, o conteúdo do array a mudou,
mas não devia ter mudado. Depois b[5] = a[2]. Mas
b[5] coincide com a[1]. Etc.
Erro de execução
•  Se o buffer overflow ocorre dentro da zona de
memória reservada para o conjunto das variáveis da
função, a memória fica corrompida, mas o programa
continua,“alegremente”.
•  Mas se o buffer overflow sai dessa zona, então
ocorre um erro de execução:
•  O erro ocorre à saída da função, porque a
informação de retorno está estragada.
18/12/14 Programação Imperativa 151
$ ./a.out
10 20 30 40 50 60 70 80 90 100 110 120
10 20 30 40 40 30 20 10 90 100 110 120
120 110 100 90 10 20 30 40 40 30 20 10
Abort trap: 6
Em Windows, apareceu-me uma janela avisando:
“a.exe has stopped working, etc.”
O C é assim mesmo
•  Em linguagens mais modernas, ocorre um erro de
execução “index out of bounds” quando tentamos
aceder a um array fora do intervalo dos índices.
•  Em C não.
•  Em C, um array é apenas um pedaço de memória: o
programa em execução sabe onde começa cada array
(na posição de índice 0), mas não sabe onde acaba.
•  A capacidade de cada array não está registada na
memória, em lado nenhum.
•  Por isso, podemos ir pela memória fora, ultrapas-
sando o fim dos arrays, sem controlo.
•  O C é assim mesmo.
•  Por isso é que nós gostamos dele.
18/12/14 Programação Imperativa 152
Programação Imperativa
Lição n.º 9
Estatísticas
Estatísticas
•  Soma, média, máximo, mínimo de arrays.
•  Redirigindo o input.
•  Argumento do máximo, do mínimo.
•  Testes unitários.
18/12/14 Programação Imperativa 154
Ler e escrever arrays de doubles
•  As funções para ler e escrever arrays de double são
parecidas com as usadas com arrays de int:
18/12/14 Programação Imperativa 155
int doubles_get(double *a)
{
int result = 0;
double x;
while (scanf("%lf", &x) != EOF)
a[result++] = x;
return result;
}
void doubles_println_basic(const double *a, int n)
{
if (n > 0)
{
printf("%g", a[0]);
for (int i = 1; i < n; i++) // i = 1
printf(" %g", a[i]);
}
printf("n");
}
Testando a leitura e a escrita
•  Eis uma função de teste:
18/12/14 Programação Imperativa 156
void test_doubles_get()
{
double a[1000];
int n = doubles_get(a);
doubles_println_basic(a, n);
}
$ ./a.out
6.3 0 56 -120.5 8 1.333
34.1 1.41 3.1415926 -999
6.3 0 56 -120.5 8 1.333 34.1 1.41 3.14159 -999
$ ./a.out A
1 3 5 8.111 9 10.7
1 3 5 8.111 9 10.7
$
No primeiro teste, os números para o
array foram escritos em duas linhas.
Contagem
•  Problema: quantos elementos do array têm um
valor dado?
18/12/14 Programação Imperativa 157
int doubles_count(const double *a, int n, int x)
{
int result = 0;
for (int i = 0; i < n; i++)
if (a[i] == x)
result++;
return result;
}
Repare na conjugação for-if: só alguns
elementos do array interessam, por
assim dizer.
Testando a contagem
•  Aceitamos o valor de referência, depois o
array até ao fim dos dados, operamos e
mostramos o resultado:
18/12/14 Programação Imperativa 158
void test_doubles_count(void)
{
double x;
scanf("%lf", &x);
double a[1000];
int n = doubles_get(a);
int z = doubles_count(a, n, x);
printf("%dn", z);
}
$ ./a.out
4
7 4 9 4 4 2 0 4 1 4 8 5 4 9 9
6
$ ./a.out
7
9 1 2 71 17 1 5
0
Problema prático
•  Quantos dias choveu em Faro este ano?
•  Temos o registo da precipitação em Faro, em
cada dia, desde 1 de Janeiro até 11 de Outubro,
deste ano.
•  O ficheiro tem um número por linha,
representando a precipitação, dia a dia.
•  Eis uma função que realiza a tarefa pedida:
18/12/14 Programação Imperativa 159
void task_rainy_days(void)
{
double p[50000]; // not more than 50000
int n = doubles_get(p);
int z = n - doubles_count(p, n, 0.0);
printf("%dn", z);
}
0.2
0
0.2
4
0
0
0
0
0
0
0
0
6
0.2
0
0.5
16.7
1
4
0
0
7.8
…
Correndo na consola
•  Podemos introduzir os dados com copy-paste,
a partir do ficheiro:
18/12/14 Programação Imperativa 160
$ ./a.out
0.2
0
0.2
4
0
2
7.8
0
0
…
0
0
0
2
14.9
0
44
Estes números todos (mais de 250)
terão sido metidos na consola com
copy-paste, o que não é muito prático.
O programa escreveu 44, que é o
número de dia de chuva em Faro,
este ano, até agora.
Redirigindo o input
•  É mais prático redirigir o input, instruindo na
linha de comando o programa para ir buscar
os dados ao ficheiro, em vez de os aceitar a
partir do teclado.
•  Observe:
18/12/14 Programação Imperativa 161
$ ./a.out < chuva_faro.txt
44
Estamos a supor que o ficheiro chuva_faro.txt,
que contém os dados, está da diretoria corrente,
a mesma que contém o ficheiro executável a.out.
Mas isso nem sempre é prático.
Diretorias de dados
•  Guardaremos os dados de cada problema numa
diretoria própria, dentro da diretoria work, a qual
está ao lado da diretoria sources:
18/12/14 Programação Imperativa 162
•  Assim, tipicamente, para
correr programas,
colocamo-nos na diretoria
de dados e chamamos o
programa que está na
diretoria sources:
$ ../../sources/a.out < chuva_faro.txt
44
Quer dizer, a partir de agora trabalharemos com
duas janelas de comando: uma para compilar, na
diretoria sources; e outra para correr programas, na
diretoria de dados.
Soma do array
•  A soma é um indicador estatístico importante:
18/12/14 Programação Imperativa 163
double doubles_sum(const double *a, int n)
{
double result = 0;
for (int i = 0; i < n; i++)
result += a[i];
return result;
}
void test_doubles_sum(void)
{
double a[1000];
int n = doubles_get(a);
int z = doubles_sum(a, n);
printf("%dn", z);
}
$ ./a.out
5 7 3 1
16
$ ./a.out
1 1 1 1 1 1 1 2 2 2 2 2 2 2
21
Em cada passo, o valor de result é a soma
“parcial”, isto é, a soma de todos os
valores observado “até agora”.
Testes unitários
•  Em vez de correr os testes interativamente, na janela
de comando, por vezes é mais prático e mais seguro
ter um conjunto de testes fixos, programados em
funções de teste unitário:
18/12/14 Programação Imperativa 164
void unit_test_doubles_sum(void)
{
double a1[8] = {6,7,1,8, 9,3,3,5};
assert(doubles_sum(a1, 8) == 42);
assert(doubles_sum(a1, 4) == 22);
assert(doubles_sum(a1, 2) == 13);
assert(doubles_sum(a1, 1) == 6);
assert(doubles_sum(a1, 0) == 0);
double a2[10] = {1,5,9,13, 17,21,25,29, 33,37};
assert(doubles_sum(a2, 10) == 190);
assert(doubles_sum(a2, 5) == 45);
}
Ao primeiro assert
que falhe, o
programa termina,
com uma
mensagem de erro
que indica a linha
culpada.
Teste unitário da função doubles_count
•  Cada função importante virá acompanhada do seu
teste unitário.
•  Eis o teste unitário da função doubles_count:
18/12/14 Programação Imperativa 165
void unit_test_doubles_count(void)
{
double a1[16] = {6,7,1,8, 9,3,3,5, 6,7,3,9, 6,1,1,1};
assert(doubles_count(a1, 16, 1) == 4);
assert(doubles_count(a1, 16, 9) == 2);
assert(doubles_count(a1, 16, 2) == 0);
assert(doubles_count(a1, 8, 1) == 1);
assert(doubles_count(a1, 8, 2) == 0);
assert(doubles_count(a1, 0, 6) == 0);
}
É verdade: os testes ocupam mais código do que
as funções de cálculo propriamente ditas.
Correndo os testes unitários
•  Reunimos todos os testes unitário numa
função unit_tests, que será chamada na função
main, logo no início:
18/12/14 Programação Imperativa 166
void unit_tests(void)
{
unit_test_doubles_count();
unit_test_doubles_sum();
// ...
}
int main(void)
{
unit_tests();
// ...
return 0;
}
Assim, de cada vez que corremos
o programa, corremos os testes
todos, automaticamente. Se
houver azar, veremos logo.
Média
•  A partir da soma, calcula-se a média:
18/12/14 Programação Imperativa 167
double doubles_mean(const double *a, int n)
{
return doubles_sum(a, n) / n;
}
Máximo de um array
•  Calcular o valor do maior elemento presente no
array é um problema clássico.
•  Se o array não for vazio, podemos programar
assim:
•  E se o array puder ser vazio?
18/12/14 Programação Imperativa 168
double doubles_max_non_empty(const double *a, int n)
{
assert(n > 0);
double result = a[0];
for (int i = 1; i < n; i++) // i = 1
if (result < a[i])
result = a[i];
return result;
}
Em cada passo, o valor de result é
o máximo “parcial”, isto é, o maior
valor observado “até agora”.
Se n for zero, a asserção falha e o
program estoira.
Máximo de um array, caso geral
•  Convencionamos que o máximo de um array
vazio é menos infinito.
•  Observe:
18/12/14 Programação Imperativa 169
double doubles_max(const double *a, int n)
{
double result = -INFINITY;
for (int i = 0; i < n; i++)
if (result < a[i])
result = a[i];
return result;
}
Testes unitários da função doubles_max
18/12/14 Programação Imperativa 170
void unit_test_doubles_max(void)
{
double a1[16] = {6,7,3,8, 9,3,3,5, 6,7,3,9, 6,1,8,3};
assert(doubles_max(a1, 16) == 9);
assert(doubles_max(a1, 4) == 8);
assert(doubles_max(a1, 1) == 6);
double a2[10] = {32,67,81,23, 27,12,90,13, 75,13};
assert(doubles_max(a2, 10) == 90);
assert(doubles_max(a2, 6) == 81);
assert(isinf(doubles_max(a2, 0)));
double a3[5] = {7e15,3e18,2e14,4e22,3e13};
assert(doubles_max(a3, 5) == 4e22);
double a4[5] = {7e-153,3e-185,2e-140,9e-225,3e-213};
assert(doubles_max(a4, 5) == 2e-140);
double a5[5] = {-7e200,-3e185,-2e240,-7e225,-3e280};
assert(doubles_max(a5, 5) == -3e185);
}
Os testes unitário são longos e chatos, mas utilíssimos!
Argumento do máximo
•  Por vezes, não nos interessa o máximo, mas
sim a sua posição no array:
18/12/14 Programação Imperativa 171
int doubles_argmax(const double *a, int n)
{
assert(n > 0);
int result = 0;
double m = a[0];
for (int i = 1; i < n; i++) // i = 1
if (m < a[i])
{
result = i;
m = a[result];
}
return result;
}
Atenção: esta função só pode ser
usada com arrays não vazios.
Habilidades com o C
•  As duas instruções dentro do if podem juntar-
se numa só:
18/12/14 Programação Imperativa 172
int doubles_argmax(const double *a, int n)
{
assert(n > 0);
int result = 0;
double m = a[0];
for (int i = 1; i < n; i++) // i = 1
if (m < a[i])
m = a[result = i];
return result;
}
OK, mas não abusemos...
Mínimo, argumento do mínimo
•  São parecidas com as anteriores:
18/12/14 Programação Imperativa 173
double doubles_min(const double *a, int n)
{
double result = +INFINITY;
for (int i = 0; i < n; i++)
if (result > a[i])
result = a[i];
return result;
}
int doubles_argmin(const double *a, int n)
{
assert(n > 0);
int result = 0;
double m = a[0];
for (int i = 1; i < n; i++) // i = 1
if (m > a[i])
m = a[result = i];
return result;
}
Testes unitários para todas
•  Todas estas funções têm o seu teste unitário:
18/12/14 Programação Imperativa 174
void unit_tests(void)
{
unit_test_doubles_count();
unit_test_doubles_sum();
unit_test_doubles_max();
unit_test_doubles_min();
unit_test_doubles_argmax();
unit_test_doubles_argmin();
}
int main(void)
{
unit_tests();
// ...
return 0;
}
Programação Imperativa
Lição n.º 10
Buscas
Buscas
•  Buscas lineares em arrays.
•  Operadores lógicos.
•  Igualdade de arrays.
•  Reabrindo a consola.
18/12/14 Programação Imperativa 176
Problema da busca
•  Existe no array um elemento com um dado
valor?
•  A resposta é sim ou não, representados em C
por 1 e 0, respetivamente.
•  Alternativamente: qual a posição no array do
primeiro elemento com um dado valor?
•  Neste caso, a resposta é um número inteiro.
•  E que resposta devemos dar quando não
existe nenhum elemento com o valor dado?
•  Por convenção, quando o valor procurado não
existe, a resposta inequívoca é -1.
18/12/14 Programação Imperativa 177
Teste unitário
•  Eis o protótipo da função de busca em arrays de int:
•  Podemos escrever já o teste unitário:
18/12/14 Programação Imperativa 178
int ints_find(const int *a, int n, int x);
void unit_test_ints_find(void)
{
int a[8] = {6,2,9,1, 4,2,7,5};
assert(ints_find(a, 8, 9) == 2);
assert(ints_find(a, 8, 5) == 7);
assert(ints_find(a, 8, 6) == 0);
assert(ints_find(a, 8, 3) == -1);
assert(ints_find(a, 4, 9) == 2);
assert(ints_find(a, 4, 5) == -1);
assert(ints_find(a, 4, 6) == 0);
assert(ints_find(a, 8, 3) == -1);
assert(ints_find(a, 1, 9) == -1);
assert(ints_find(a, 1, 6) == 0);
assert(ints_find(a, 0, 6) == -1);
assert(ints_find(a, 0, 4) == -1);
}
Note que o teste
unitário serve também
para esclarecer o
significado da função.
A função devolve o
índice da primeira
ocorrência do valor x
no array a (cujo
tamanho é n) ou -1, se
não houver.
Função ints_find
•  Esta função é exemplar:
18/12/14 Programação Imperativa 179
int ints_find(const int *a, int n, int x)
{
for (int i = 0; i < n; i++)
if (a[i] == x)
return i;
return -1;
}
Repare bem: dois returns, o primeiro
dentro do ciclo, o seguindo após o
ciclo. Esta é a única função em que
usamos esta técnica.
Aplicação: validação de números de aluno
•  Queremos um programa para validar
interativamente números de aluno.
•  O problema tem acesso a um ficheiro
com os números de todos os alunos
inscritos.
•  Se o número for válido, o programa
escreve 1; se não, escreve 0.
•  O ficheiro é lido por redireção do input.
18/12/14 Programação Imperativa 180
...
44928
48075
50816
51732
52395
50076
45934
52263
50110
52875
51493
51990
44949
48272
52722
49728
52260
52319
51749
...
Questão prévia
•  Se o input é redirigido para o ficheiro, como
podemos depois usar a janela de comando
para interagir com o programa?
•  Ora bem: temos de “reabrir a consola”!
•  Isso faz-se assim em Windows:
•  E assim em Unix:
18/12/14 Programação Imperativa 181
freopen("/dev/tty", "r", stdin);
freopen("CON", "r", stdin);
Tarefa de validação
•  Observe:
18/12/14 Programação Imperativa 182
void task_validate_student(void)
{
int a[500];
int n = ints_get(a);
freopen("/dev/tty", "r", stdin); // Unix
// freopen("CON", "r", stdin); // Windows
int x;
while (scanf("%d", &x) != EOF)
{
int z = ints_find(a, n, x);
printf("%dn", z != -1);
}
}
Repare: a expressão z != -1 vale 1 se z for diferente de
-1 e vale 0 se z for igual a -1, tal como convém.
Correndo na consola
•  O comando que invoca o programa realiza a
redireção do input:
18/12/14 Programação Imperativa 183
$ ../../sources/a.out < inscritos.txt
33445
0
45634
0
52092
1
52080
1
50000
0
41895
1
52230
0
40758
1
$
O ficheiro inscritos.txt está arrumado
de acordo com as nossas convenções,
numa subdiretoria da diretoria work, a
qual está a par da diretoria sources,
onde reside o executável a.out.
Busca do fim para o princípio
•  Por vezes, queremos a última ocorrência.
•  Nesse caso, procuramos do fim para o
princípio:
18/12/14 Programação Imperativa 184
int ints_find_last(const int *a, int n, int x)
{
int result = n-1;
while (result >= 0 && a[result] != x)
result--;
return result;
} Note que neste caso não há interesse
em usar o esquema dos dois returns,
pois a variável result tomará o valor -1
“naturalmente”, quando a busca falhar.
Operadores lógicos
18/12/14 Programação Imperativa 185
&& conjunção
|| disjunção
! negação
Variante: obter todas as ocorrências
•  Se queremos não a primeira ocorrência mas
sim todas as ocorrências, precisamos de um
array:
18/12/14 Programação Imperativa 186
int ints_find_all(const int *a, int n, int x, int *b)
{
int result = 0;
for (int i = 0; i < n; i++)
if (a[i] == x)
b[result++] = i;
return result;
} Note que b é um array de índices.
Ficará vazio se x não ocorrer em a.
Array das primeiras ocorrências
•  Em geral, cada valor pode ocorrer várias vezes.
•  Queremos agora calcular o array das primeiras
ocorrências de cada valor, dito a “essência” do
array:
18/12/14 Programação Imperativa 187
int ints_nub(const int *a, int n, int *b)
{
int result = 0;
for (int i = 0; i < n; i++)
if (ints_find(b, result, a[i]) == -1)
b[result++] = a[i];
return result;
} Palavras para quê?
Testando ints_nub
•  Eis uma função de teste, como habitualmente:
18/12/14 Programação Imperativa 188
void test_ints_nub(void)
{
int a[1000];
int n = ints_get(a);
int b[1000];
int m = ints_nub(a, n, b);
ints_println_basic(b, m);
}
$ ./a.out
5 7 5 5 2 7 3 2 2 3 3 1 1 7
5 7 2 3 1
$ ./a.out
3 3 3 3 3 4 4 4 3 3 3 5 5 5 4 4 4 1 1
3 4 5 1
$
Aplicação: quantos alunos na aula prática?
•  Queremos saber quantos alunos vieram à aula,
com base no registo das submissões ao Mooshak.
•  De cada submissão, retiramos o número de aluno
e guardamos os números num ficheiro.
•  Lemos para um array e fazemos ints_nub:
18/12/14 Programação Imperativa 189
void task_students_in_lab(void)
{
int a[500];
int n = ints_get(a);
int b[500];
int m = ints_nub(a, n, b);
printf("%dn", m);
}
$ ../../sources/a.out < submissoes.txt
25
$
...
49863
49863
51767
52727
51767
51767
52495
51767
51767
49863
49863
49863
45934
51493
45934
51493
50372
50372
49863
50372
50372
...
Igualdade de arrays
•  Para verificar se dois arrays a e b são iguais, isto é, se
têm os mesmos elementos, pela mesma ordem, não
basta escrever a == b.
•  É preciso programar uma função ad hoc:
18/12/14 Programação Imperativa 190
int ints_equal_arrays(
const int *a, const int n, const int *b, int m)
{
int result = n == m;
int i = 0;
while (result && i < n)
if (a[i] != b[i])
result = 0;
else
i++;
return result;
}
Se os tamanhos forem diferentes, os
arrays não são iguais. Sendo os tamanhos
iguais, procura-se o primeiro par de
elementos diferentes. Logo que se
encontre, sabe-se que os arrays não são
iguais. Não encontrando, os arrays são
iguais.
Ao longo do ciclo, o valor da variável result
significa “os arrays são iguais até agora”, por assim
dizer.
Testando a igualdade de arrays
•  Se lermos os arrays com ints_get, temos de
reabrir a consola:
18/12/14 Programação Imperativa 191
void test_ints_equal_arrays(void)
{
int a[1000];
int n = ints_get(a);
freopen("/dev/tty", "r", stdin); // Unix
// freopen("CON", "r", stdin); // Windows
int b[1000];
int m = ints_get(b);
int z = ints_equal_arrays(a, n, b, m);
printf("%dn", z);
}
Testes unitários com igualdade de arrays
•  Observe, por exemplo:
18/12/14 Programação Imperativa 192
void unit_test_ints_nub(void)
{
int a1[12] = {6,2,6,9, 4,2,9,9, 9,2,1,2};
int b1[12];
int m1 = ints_nub(a1, 12, b1);
int z1[5] = {6,2,9,4,1};
assert(ints_equal_arrays(b1, m1, z1, 5));
int a2[6] = {1,2,3,3,2,1};
int b2[6];
int m2 = ints_nub(a2, 6, b2);
int z2[3] = {1,2,3};
assert(ints_equal_arrays(b2, m2, z2, 3));
int a3[5] = {8,8,8,8,8};
int b3[5];
int m3 = ints_nub(a3, 5, b3);
assert(m3 == 1 && b3[0] == 8);
}
Programação Imperativa
Lição n.º 11
Subarrays
Subarrays
•  Subarrays em C.
•  Grupos.
•  Remoção de duplicados.
18/12/14 Programação Imperativa 194
Subarrays
•  Atenção: não há subarrays em C.
•  O que há é uma maneira de nos referirmos,
nas funções, a uma parte de um array.
•  Na verdade, já observámos isso, por exemplo
nas funções de teste unitário:
18/12/14 Programação Imperativa 195
void unit_test_doubles_sum(void)
{
double a1[8] = {6,7,1,8, 9,3,3,5};
assert(doubles_sum(a1, 8) == 42);
assert(doubles_sum(a1, 4) == 22);
assert(doubles_sum(a1, 2) == 13);
assert(doubles_sum(a1, 1) == 6);
assert(doubles_sum(a1, 0) == 0);
}
O array a tem 8 elementos,
mas ao somar os 4 primeiros
elementos é como se
estivéssemos a somar todos
os elementos do subarray
inicial de a, com 4 elementos.
Subarrays gerais
•  Também podemos ter subarrays não iniciais, como
ilustram as seguintes funções de teste unitário:
18/12/14 Programação Imperativa 196
void unit_test_subarrays_sum(void)
{
double a[8] = {4,9,4,4, 5,2,7,5};
assert(doubles_sum(a, 8) == 40);
assert(doubles_sum(a, 5) == 26);
assert(doubles_sum(a+3, 5) == 23);
assert(doubles_sum(a+2, 4) == 15);
}
void unit_test_subarrays_max(void)
{
double a[8] = {4,9,4,4, 5,2,7,5};
assert(doubles_max(a, 8) == 9);
assert(doubles_max(a, 5) == 9);
assert(doubles_max(a+3, 5) == 7);
assert(doubles_max(a+2, 4) == 5);
}
Por exemplo, a expressão
doubles_sum(a+3, 5)
representa a soma dos valores
de a[3], a[4], a[5], a[6] e a[7].
Por exemplo, a expressão
doubles_max(a+2, 4)
representa o máximo dos
valores de a[2], a[3], a[4] e
a[5].
a + k
•  Em geral, sendo a um array e k um número
inteiro, a expressão a + k representa o
subarray de a que começa no elemento a[k].
•  Tal como com os arrays em geral, ao
especificarmos um subarray, normalmente
indicamos o seu tamanho, isto é, o número de
elementos que queremos processar.
•  Frequentemente, interessa-nos o resto do
array; por outras palavras, se o array a tiver n
elementos, o array a + k terá n – k elementos.
18/12/14 Programação Imperativa 197
Exemplos: soma recursiva, máximo recursivo
•  Usando subarrays, podemos processar arrays
recursivamente.
•  Observe, com atenção:
18/12/14 Programação Imperativa 198
double doubles_sum_r(double *a, int n)
{
return n == 0 ? 0 : a[0] + doubles_sum_r(a+1, n-1);
}
double doubles_max_r(double *a, int n)
{
return n == 0 ? -INFINITY
: max(a[0], doubles_max_r(a+1, n-1));
}
Em C, normalmente não se programa assim,
mas a técnica é interessante e válida, em geral.
Aplicação: problema da via do infante
•  Dispomos de um ficheiro com o número de
carros que passaram no pórtico de Loulé,
minuto a minuto, num certo dia.
•  Queremos saber qual foi o quarto de hora
com mais trânsito.
•  Solução: ler o ficheiro para um array e somar
os sucessivos subarrays de tamanho 15,
guardando os resultados noutro array.
•  Depois, obter o argumento do máximo
neste array e identificar o quarto de hora
respetivo.
18/12/14 Programação Imperativa 199
...
16
5
4
10
7
3
18
16
7
15
7
18
20
9
3
4
16
3
0
20
9
15
...
Somar de 15 em 15
•  Queremos somar os números de carros que
passaram em cada minuto, para cada quarto de
hora:
18/12/14 Programação Imperativa 200
int ints_sums_by_15(const int *a, int n, int *b)
{
int result = 0;
for (int i = 0; i < n; i += 15)
b[result++] = ints_sum(a+i, min(15, n - i));
return result;
} Repare na utilização da função
min, para o caso geral de n não
ser múltiplo de 15.
A variável de controlo cresce de 15 em 15.
Somar grupos de comprimento fixo
•  Com um pouco mais de esforço,
programamos uma função mais geral, que
soma grupos de comprimento dado:
18/12/14 Programação Imperativa 201
int ints_sums_by_tuple
(const int *a, int n, int x, int *b)
{
int result = 0;
for (int i = 0; i < n; i += x)
b[result++] = ints_sum(a+i, min(x, n - i));
return result;
}
Função de teste
•  Observe:
18/12/14 Programação Imperativa 202
void test_infant(void)
{
int a[1440];
int n = ints_get(a);
int b[1440];
int m = ints_sums_by_tuple(a, n, 15, b);
ints_println_basic(b, m);
int z = ints_argmax(b, m);
printf("%d %dn", z, b[z]);
printf("%d %dn", z / 4, z % 4);
}
$ ../../sources/a.out < infant_data.txt
34 35 31 18 12 15 16 17 12 16 15 13 15
16 14 15 11 12 14 15 17 16 25 29 37 23
42 65 43 94 76 110 106 104 102 144 167
152 164 190 240 230 176 157 177 148 139
155 201 180 146 160 174 168 170 220 146
144 145 135 145 133 187 150 126 122 147
144 152 192 201 182 195 153 238 242 166
205 168 182 147 113 132 121 186 128 138
145 143 103 106 60 65 49 48 24
75 242
18 3
$
Para estes dados, o quarto de hora
com mais trânsito foi o que começou
às 18:45.
Problema dos grupos
•  Dado um array de double, construir um outro array
(de int) com os comprimentos dos grupos de
elementos consecutivos iguais.
18/12/14 Programação Imperativa 203
void unit_test_doubles_groups(void)
{
double a1[16] = {4,9,4,4, 4,7,7,7, 7,7,8,6, 6,6,6,4};
int b1[16];
int z1[7] = {1,1,3,5,1,4,1};
int m1 = doubles_groups(a1, 16, b1);
assert(ints_equal_arrays(b1, m1, z1, 7));
double a2[8] = {4,4,4,4, 4,4,4,4};
int b2[8];
int z2[1] = {8};
int m2 = doubles_groups(a2, 8, b2);
assert(ints_equal_arrays(b2, m2, z2, 1));
}
int doubles_groups(const double *a, int n, int *b);
Contar enquanto...
•  Vamos basear-nos numa função que conta os
elementos à cabeça do array que têm um dado valor.
•  A função conta “enquanto” os elementos forem
iguais ao valor dado:
18/12/14 Programação Imperativa 204
void unit_test_doubles_count_while(void)
{
double a[16] = {4,4,4,3, 5,9,9,5, 5,5,5,5, 5,5,1,1};
assert(doubles_count_while(a, 16, 4) == 3);
assert(doubles_count_while(a, 16, 7) == 0);
assert(doubles_count_while(a+4, 12, 5) == 1);
assert(doubles_count_while(a+4, 12, 2) == 0);
assert(doubles_count_while(a+8, 8, 5) == 6);
assert(doubles_count_while(a+8, 8, 3) == 0);
assert(doubles_count_while(a+14, 2, 1) == 2);
assert(doubles_count_while(a+14, 2, 3) == 0);
}
int doubles_count_while(const double *a, int n, double x);
Função doubles_count_while
•  Na verdade, é uma variante da função de
busca:
18/12/14 Programação Imperativa 205
int doubles_count_while
(const double *a, int n, double x)
{
int result = 0;
while (result < n && a[result] == x)
result++;
return result;
}
Função doubles_groups
•  Por cada grupo, acrescenta-se o comprimento
do grupo ao array de saída e avança-se no
array de entrada:
18/12/14 Programação Imperativa 206
int doubles_groups(const double *a, int n, int *b)
{
int result = 0;
int i = 0;
while (i < n)
{
int z = doubles_count_while(a+i, n-i, a[i]);
b[result++] = z;
i += z;
}
return result;
}
Em cada passo do ciclo, detetamos um novo
grupo, à cabeça do subarray seguinte. Esse
grupo é formado pelos elementos que são
iguais ao primeiro do grupo.
Aprenda muito bem
esta função!
Problema da remoção de duplicados
•  Dado um array de double, construir um outro array
(de double) com um exemplar de cada grupo de
elementos consecutivos iguais.
18/12/14 Programação Imperativa 207
void unit_test_doubles_unique(void)
{
double a1[16] = {4,9,4,4, 4,7,7,7, 7,7,8,6, 6,6,6,4};
double b1[16];
double z1[7] = {4,9,4,7,8,6,4};
int m1 = doubles_unique(a1, 16, b1);
assert(doubles_equal_arrays(b1, m1, z1, 7));
double a2[8] = {4,4,4,4, 4,4,4,4};
double b2[8];
double z2[1] = {4};
int m2 = doubles_unique(a2, 8, b2);
assert(doubles_equal_arrays(b2, m2, z2, 1));
}
int doubles_unique(const double *a, int n, double *b);
Não confunda com a função “nub”.
Essa guarda a primeira ocorrência no
array. Esta guarda um exemplar de
cada grupo de elementos
consecutivos iguais.
Função doubles_unique
•  Por cada grupo, acrescenta-se um elemento
do grupo ao array de saída e avança-se no
array de entrada:
18/12/14 Programação Imperativa 208
int doubles_unique(const double *a, int n, double *b)
{
int result = 0;
int i = 0;
while (i < n)
{
int z = doubles_count_while(a+i, n-i, a[i]);
b[result++] = a[i];
i += z;
}
return result;
}
É praticamente igual à
outra, não é?
Versões recursivas
•  As versões recursivas das funções groups e unique
também são muito interessantes:
18/12/14 Programação Imperativa 209
int doubles_groups_r(const double *a, int n, int *b)
{
int result = 0;
if (n > 0)
{
int z = doubles_count_while(a, n, a[0]);
b[0] = z;
result = 1 + doubles_groups_r(a+z, n-z, b+1);
}
return result;
}
int doubles_unique_r(const double *a, int n, double *b)
{
int result = 0;
if (n > 0)
{
int z = doubles_count_while(a, n, a[0]);
b[0] = a[0];
result = 1 + doubles_unique_r(a+z, n-z, b+1);
}
return result;
}
a[0] ≡ *a
•  Em C, não se escreve a[0]. Em vez disso
escreve-se *a, que significa o mesmo e usa
menos carateres...
•  Por exemplo:
18/12/14 Programação Imperativa 210
int doubles_unique_r2(const double *a, int n, double *b)
{
int result = 0;
if (n > 0)
{
int z = doubles_count_while(a, n, *a);
*b = *a;
result = 1 + doubles_unique_r2(a+z, n-z, b+1);
}
return result;
}
Vá-se habituando...
Programação Imperativa
Lição n.º 12
Arrays ordenados
Arrays ordenados
•  Renque.
•  Busca dicotómica.
•  Método da bisseção.
18/12/14 Programação Imperativa 212
Problema do renque
•  O renque de um valor num array é o número
de elementos do array cujo valor é menor que
esse valor:
18/12/14 Programação Imperativa 213
int ints_rank_general(const int *a, int n, int x);
void unit_test_rank_general(void)
{
int a[10] = {8,3,9,8,4, 4,2,7,5,7};
assert(ints_rank_general(a, 10, 5) == 4);
assert(ints_rank_general(a, 10, 12) == 10);
assert(ints_rank_general(a, 10, 1) == 0);
assert(ints_rank_general(a, 10, 2) == 0);
assert(ints_rank_general(a, 10, 3) == 1);
assert(ints_rank_general(a, 10, 7) == 5);
assert(ints_rank_general(a, 5, 7) == 2);
assert(ints_rank_general(a, 5, 3) == 0);
assert(ints_rank_general(a, 5, 9) == 4);
assert(ints_rank_general(a, 1, 5) == 0);
assert(ints_rank_general(a, 1, 9) == 1);
assert(ints_rank_general(a, 0, 5) == 0);
}
Chamamos renque
geral porque se pode
aplicar a um array
qualquer. Para arrays
ordenados, usaremos
funções especializadas.
int ints_rank_general_r(const int *a, int n, int x)
{
int result = 0;
if (n > 0)
result = (*a < x) + ints_rank_general_r(a+1, n-1, x);
return result;
}
Renque geral
•  Programa-se nas calmas:
•  A versão recursiva também é interessante:
18/12/14 Programação Imperativa 214
int ints_rank_general(const int *a, int n, int x)
{
int result = 0;
for (int i = 0; i < n; i++)
if (a[i] < x)
result++;
return result;
}
É uma variante da função
de contagem.
Note bem: o valor aritmético das expressões
lógicas é 0 ou 1.
Arrays ordenados
•  Um array está ordenado se o valor de cada elemento
é menor ou igual ao valor do elemento seguinte.
•  Essa propriedade é representada pela seguinte função
lógica:
18/12/14 Programação Imperativa 215
void unit_test_ints_is_sorted(void)
{
int a[10] = {1,2,5,5,5, 6,8,8,9,9};
assert(ints_is_sorted(a, 10));
assert(ints_is_sorted(a, 1));
assert(ints_is_sorted(a, 0));
int b[10] = {3,5,5,2,4, 4,8,8,2,5};
assert(!ints_is_sorted(b, 10));
assert(ints_is_sorted(b, 3));
assert(!ints_is_sorted(b, 5));
assert(ints_is_sorted(b+3, 5));
assert(!ints_is_sorted(b+5, 5));
}
int ints_is_sorted(int *a, int n);
Função is_sorted
•  Procura-se o primeiro par de elementos
consecutivos fora de ordem:
•  Também a versão recursiva:
18/12/14 Programação Imperativa 216
int ints_is_sorted(int *a, int n)
{
for (int i = 1; i < n; i++)
if (a[i-1] > a[i])
return 0;
return 1;
}
int ints_is_sorted_r(int *a, int n)
{
return n <= 1 || (*a <= a[1] && ints_is_sorted_r(a+1, n-1));
}
Um array está ordenado se o seu tamanho for menor ou igual a 1 ou, sendo maior
que 1, se o valor do primeiro elemento for menor ou igual ao do segundo e o
resto do array (tirando o primeiro elemento) estiver ordenado.
Renque em arrays ordenados
•  O renque geral inspeciona todos os elementos
do array e conta aqueles que são menores que o
valor dado.
•  Se o array estiver ordenado, conseguimos calcular
o renque sem inspecionar os elementos todos.
•  Aliás, conseguimos fazê-lo inspecionando
relativamente poucos elementos.
•  Vejamos como.
•  Por hipótese, temos um array a, ordenado, com
tamanho n, e é dado um valor x: queremos
calcular o número de elementos de a cujo valor é
menor que x.
18/12/14 Programação Imperativa 217
Calculando o renque em arrays ordenados
•  Tomemos um elemento qualquer de a, a[m].
•  Se x<=a[m], então x<=a[m+1], x<=a[m+2], etc., pois
o array está ordenado; logo, todos os elementos de a
cujo valor é menor que x estão à esquerda de a[m].
•  Inversamente, se x>a[m], então x>a[m-1], x>a[m-2],
etc., porque o array está ordenado; logo, o valor de
cada um dos elementos à esquerda de a[m] é menor
que x e o valor de a[m] também.
•  Sendo assim, no primeiro caso, basta contar os
elementos de valor menor que x no subarray inicial
com m elementos; no segundo, há pelo menos m+1
elementos com valor menor que x, a que se juntam
os elementos menores que x no subarray a+(m+1).
18/12/14 Programação Imperativa 218
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI
Cálculo nota final LEI

Weitere ähnliche Inhalte

Was ist angesagt?

Aula 3 - Lógica de Programação
Aula 3 - Lógica de ProgramaçãoAula 3 - Lógica de Programação
Aula 3 - Lógica de ProgramaçãoInstituto CENTEC
 
Aula01 introducao operacoes
Aula01   introducao operacoesAula01   introducao operacoes
Aula01 introducao operacoesYuri Passos
 
Conceitos Iniciais de Linguagens de Programação
Conceitos Iniciais de Linguagens de ProgramaçãoConceitos Iniciais de Linguagens de Programação
Conceitos Iniciais de Linguagens de ProgramaçãoSidney Roberto
 
LIA - Linguagem Interpretada de Algoritmos
LIA - Linguagem Interpretada de AlgoritmosLIA - Linguagem Interpretada de Algoritmos
LIA - Linguagem Interpretada de AlgoritmosRafael Martins
 
4 introdução ao paradigma funcional
4 introdução ao paradigma funcional4 introdução ao paradigma funcional
4 introdução ao paradigma funcionalPéricles Miranda
 
Introdução ao paradigma imperativo
Introdução ao paradigma imperativoIntrodução ao paradigma imperativo
Introdução ao paradigma imperativoTony Alexander Hild
 
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...Adriano Teixeira de Souza
 
Conceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoConceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoJorge Cardoso
 
Programando java independente da ide
Programando java independente da ideProgramando java independente da ide
Programando java independente da ideHeider Lopes
 
Paradigmas de Linguagens de Programação - Classificações
Paradigmas de Linguagens de Programação - ClassificaçõesParadigmas de Linguagens de Programação - Classificações
Paradigmas de Linguagens de Programação - ClassificaçõesAdriano Teixeira de Souza
 
Paradigmas de Programação - Imperativo, Orientado a Objetos e Funcional
Paradigmas de Programação - Imperativo, Orientado a Objetos e FuncionalParadigmas de Programação - Imperativo, Orientado a Objetos e Funcional
Paradigmas de Programação - Imperativo, Orientado a Objetos e FuncionalGustavo Coutinho
 
Linguagem de programação
Linguagem de programação Linguagem de programação
Linguagem de programação Marcos Gregorio
 
E14 s07-t3-algoritmos-2013-04-24 (1)
E14 s07-t3-algoritmos-2013-04-24 (1)E14 s07-t3-algoritmos-2013-04-24 (1)
E14 s07-t3-algoritmos-2013-04-24 (1)Alex Almeida
 
Gerador de Código-Objeto - Compiladores
Gerador de Código-Objeto - CompiladoresGerador de Código-Objeto - Compiladores
Gerador de Código-Objeto - CompiladoresAnderson Favaro
 

Was ist angesagt? (20)

Aula 3 - Lógica de Programação
Aula 3 - Lógica de ProgramaçãoAula 3 - Lógica de Programação
Aula 3 - Lógica de Programação
 
C 04 linguagem_de_programacao
C 04 linguagem_de_programacaoC 04 linguagem_de_programacao
C 04 linguagem_de_programacao
 
Aula01 introducao operacoes
Aula01   introducao operacoesAula01   introducao operacoes
Aula01 introducao operacoes
 
Linguagens de programação
Linguagens de programaçãoLinguagens de programação
Linguagens de programação
 
Mini Curso de C
Mini Curso de CMini Curso de C
Mini Curso de C
 
Conceitos Iniciais de Linguagens de Programação
Conceitos Iniciais de Linguagens de ProgramaçãoConceitos Iniciais de Linguagens de Programação
Conceitos Iniciais de Linguagens de Programação
 
LIA - Linguagem Interpretada de Algoritmos
LIA - Linguagem Interpretada de AlgoritmosLIA - Linguagem Interpretada de Algoritmos
LIA - Linguagem Interpretada de Algoritmos
 
4 introdução ao paradigma funcional
4 introdução ao paradigma funcional4 introdução ao paradigma funcional
4 introdução ao paradigma funcional
 
Introdução ao paradigma imperativo
Introdução ao paradigma imperativoIntrodução ao paradigma imperativo
Introdução ao paradigma imperativo
 
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...
Paradigmas de Linguagens de Programação - Quatro Paradigmas + Ambientes de Pr...
 
Conceitos Fundamentais de Programacao
Conceitos Fundamentais de ProgramacaoConceitos Fundamentais de Programacao
Conceitos Fundamentais de Programacao
 
Programando java independente da ide
Programando java independente da ideProgramando java independente da ide
Programando java independente da ide
 
Software
SoftwareSoftware
Software
 
Paradigmas de Linguagens de Programação - Classificações
Paradigmas de Linguagens de Programação - ClassificaçõesParadigmas de Linguagens de Programação - Classificações
Paradigmas de Linguagens de Programação - Classificações
 
Paradigmas de Programação - Imperativo, Orientado a Objetos e Funcional
Paradigmas de Programação - Imperativo, Orientado a Objetos e FuncionalParadigmas de Programação - Imperativo, Orientado a Objetos e Funcional
Paradigmas de Programação - Imperativo, Orientado a Objetos e Funcional
 
Linguagem de programação
Linguagem de programação Linguagem de programação
Linguagem de programação
 
Aula 1 pc - slides
Aula 1   pc - slidesAula 1   pc - slides
Aula 1 pc - slides
 
E14 s07-t3-algoritmos-2013-04-24 (1)
E14 s07-t3-algoritmos-2013-04-24 (1)E14 s07-t3-algoritmos-2013-04-24 (1)
E14 s07-t3-algoritmos-2013-04-24 (1)
 
Gerador de Código-Objeto - Compiladores
Gerador de Código-Objeto - CompiladoresGerador de Código-Objeto - Compiladores
Gerador de Código-Objeto - Compiladores
 
Linguagem da programação
Linguagem da programaçãoLinguagem da programação
Linguagem da programação
 

Andere mochten auch

Mineração de dados com a presença da deriva conceitual
Mineração de dados com a presença da deriva conceitualMineração de dados com a presença da deriva conceitual
Mineração de dados com a presença da deriva conceitualLuis Miguel Rojas Aguilera
 
Genetic Algorithm For Job Shop Problem
Genetic Algorithm For Job Shop ProblemGenetic Algorithm For Job Shop Problem
Genetic Algorithm For Job Shop Problemmarcelobeckmann
 
Inteligência Artificial: a ciência da antecipação em ambientes incertos
Inteligência Artificial: a ciência da antecipação em ambientes incertosInteligência Artificial: a ciência da antecipação em ambientes incertos
Inteligência Artificial: a ciência da antecipação em ambientes incertosCarlos R. B. Azevedo
 
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014Pedro Guerreiro
 
PLP – Paradigma de Programação Orientado a Modelos
PLP – Paradigma de Programação Orientado a ModelosPLP – Paradigma de Programação Orientado a Modelos
PLP – Paradigma de Programação Orientado a Modeloselliando dias
 
Matemática das redes - parte II
Matemática das redes - parte IIMatemática das redes - parte II
Matemática das redes - parte IIDalton Martins
 
Matemática das Redes - Parte I
Matemática das Redes - Parte IMatemática das Redes - Parte I
Matemática das Redes - Parte IDalton Martins
 
Pos Usp Ngs Big Data Parte 2
Pos Usp Ngs Big Data Parte 2Pos Usp Ngs Big Data Parte 2
Pos Usp Ngs Big Data Parte 2Beta Campos
 
Aula 13 - Algoritmos de Escalonamento
Aula 13 - Algoritmos de Escalonamento Aula 13 - Algoritmos de Escalonamento
Aula 13 - Algoritmos de Escalonamento camila_seixas
 
Como Python pode ajudar na automação do seu laboratório
Como Python pode ajudar na automação do  seu laboratórioComo Python pode ajudar na automação do  seu laboratório
Como Python pode ajudar na automação do seu laboratórioMarcel Caraciolo
 
Sistemas operacionais escalonamento de processos
Sistemas operacionais  escalonamento de processosSistemas operacionais  escalonamento de processos
Sistemas operacionais escalonamento de processosTalles Nascimento Rodrigues
 
Introdução à Teoria dos Grafos e Análise de Redes Sociais
Introdução à Teoria dos Grafos e Análise de Redes SociaisIntrodução à Teoria dos Grafos e Análise de Redes Sociais
Introdução à Teoria dos Grafos e Análise de Redes Sociaisfabiomalini
 
Técnicas para o Desenvolvimento de Cronogramas
Técnicas para o Desenvolvimento de CronogramasTécnicas para o Desenvolvimento de Cronogramas
Técnicas para o Desenvolvimento de CronogramasPeter Mello
 

Andere mochten auch (15)

Mineração de dados com a presença da deriva conceitual
Mineração de dados com a presença da deriva conceitualMineração de dados com a presença da deriva conceitual
Mineração de dados com a presença da deriva conceitual
 
4 groebner danton4 dissertacao
4 groebner danton4 dissertacao4 groebner danton4 dissertacao
4 groebner danton4 dissertacao
 
Genetic Algorithm For Job Shop Problem
Genetic Algorithm For Job Shop ProblemGenetic Algorithm For Job Shop Problem
Genetic Algorithm For Job Shop Problem
 
Alessandra henriquesferreiravc
Alessandra henriquesferreiravcAlessandra henriquesferreiravc
Alessandra henriquesferreiravc
 
Inteligência Artificial: a ciência da antecipação em ambientes incertos
Inteligência Artificial: a ciência da antecipação em ambientes incertosInteligência Artificial: a ciência da antecipação em ambientes incertos
Inteligência Artificial: a ciência da antecipação em ambientes incertos
 
Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014Algoritmos e Estruturas de Dados, edição de 2013/2014
Algoritmos e Estruturas de Dados, edição de 2013/2014
 
PLP – Paradigma de Programação Orientado a Modelos
PLP – Paradigma de Programação Orientado a ModelosPLP – Paradigma de Programação Orientado a Modelos
PLP – Paradigma de Programação Orientado a Modelos
 
Matemática das redes - parte II
Matemática das redes - parte IIMatemática das redes - parte II
Matemática das redes - parte II
 
Matemática das Redes - Parte I
Matemática das Redes - Parte IMatemática das Redes - Parte I
Matemática das Redes - Parte I
 
Pos Usp Ngs Big Data Parte 2
Pos Usp Ngs Big Data Parte 2Pos Usp Ngs Big Data Parte 2
Pos Usp Ngs Big Data Parte 2
 
Aula 13 - Algoritmos de Escalonamento
Aula 13 - Algoritmos de Escalonamento Aula 13 - Algoritmos de Escalonamento
Aula 13 - Algoritmos de Escalonamento
 
Como Python pode ajudar na automação do seu laboratório
Como Python pode ajudar na automação do  seu laboratórioComo Python pode ajudar na automação do  seu laboratório
Como Python pode ajudar na automação do seu laboratório
 
Sistemas operacionais escalonamento de processos
Sistemas operacionais  escalonamento de processosSistemas operacionais  escalonamento de processos
Sistemas operacionais escalonamento de processos
 
Introdução à Teoria dos Grafos e Análise de Redes Sociais
Introdução à Teoria dos Grafos e Análise de Redes SociaisIntrodução à Teoria dos Grafos e Análise de Redes Sociais
Introdução à Teoria dos Grafos e Análise de Redes Sociais
 
Técnicas para o Desenvolvimento de Cronogramas
Técnicas para o Desenvolvimento de CronogramasTécnicas para o Desenvolvimento de Cronogramas
Técnicas para o Desenvolvimento de Cronogramas
 

Ähnlich wie Cálculo nota final LEI

Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Robson Ferreira
 
Introdução a Linguagem C
Introdução a Linguagem CIntrodução a Linguagem C
Introdução a Linguagem CVê Rodrigues
 
Aula01 ip introducao
Aula01 ip introducaoAula01 ip introducao
Aula01 ip introducaoBerg Oliveira
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...Alex Casañas
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...Alex Casañas
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...Alex Casañas
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...Alex Casañas
 
02 Introdução à programação.pptx
02 Introdução à programação.pptx02 Introdução à programação.pptx
02 Introdução à programação.pptxwilliam Sarti José
 
C curso - notas de aula
C   curso - notas de aulaC   curso - notas de aula
C curso - notas de aulaLoAlves21
 
Apostila de Linguagem C
Apostila de Linguagem CApostila de Linguagem C
Apostila de Linguagem CDaniel Barão
 
Aula de C para Linux
Aula de C para LinuxAula de C para Linux
Aula de C para LinuxChris x-MS
 
Curso C e C++ - Teoria PDF.pdf
Curso C e C++ - Teoria PDF.pdfCurso C e C++ - Teoria PDF.pdf
Curso C e C++ - Teoria PDF.pdfJoaoAbadia2
 
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdf
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdfTREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdf
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdfssuser059c2c1
 
4-Introducao programacao-convertido.pptx
4-Introducao programacao-convertido.pptx4-Introducao programacao-convertido.pptx
4-Introducao programacao-convertido.pptxPatrícia Melo
 
Aula 01 - Introducao a C (3).pptx
Aula 01 - Introducao a C (3).pptxAula 01 - Introducao a C (3).pptx
Aula 01 - Introducao a C (3).pptxAbdulSacur2
 
Algoritmos - Paradigmas de Programação
Algoritmos - Paradigmas de ProgramaçãoAlgoritmos - Paradigmas de Programação
Algoritmos - Paradigmas de ProgramaçãoElaine Cecília Gatto
 
Lógica de programação pascal
Lógica de programação   pascalLógica de programação   pascal
Lógica de programação pascalJocelma Rios
 

Ähnlich wie Cálculo nota final LEI (20)

Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2Conceitos e técnicas de programação aula 2
Conceitos e técnicas de programação aula 2
 
01-Paradigmas.pdf
01-Paradigmas.pdf01-Paradigmas.pdf
01-Paradigmas.pdf
 
Introdução a Linguagem C
Introdução a Linguagem CIntrodução a Linguagem C
Introdução a Linguagem C
 
Aula01 ip introducao
Aula01 ip introducaoAula01 ip introducao
Aula01 ip introducao
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
 
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...Unidade 111   estrutura de dados e lógica de programação - com respostas 15-0...
Unidade 111 estrutura de dados e lógica de programação - com respostas 15-0...
 
02 Introdução à programação.pptx
02 Introdução à programação.pptx02 Introdução à programação.pptx
02 Introdução à programação.pptx
 
Introdução à Linguagem C
Introdução à Linguagem CIntrodução à Linguagem C
Introdução à Linguagem C
 
C curso - notas de aula
C   curso - notas de aulaC   curso - notas de aula
C curso - notas de aula
 
Apostila c
Apostila cApostila c
Apostila c
 
Apostila de Linguagem C
Apostila de Linguagem CApostila de Linguagem C
Apostila de Linguagem C
 
Aula de C para Linux
Aula de C para LinuxAula de C para Linux
Aula de C para Linux
 
Curso C e C++ - Teoria PDF.pdf
Curso C e C++ - Teoria PDF.pdfCurso C e C++ - Teoria PDF.pdf
Curso C e C++ - Teoria PDF.pdf
 
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdf
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdfTREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdf
TREINAMENTO EM LOGICA DE PROGRAMAÇÃO (Sandra Rita) (z-lib.org).pdf
 
4-Introducao programacao-convertido.pptx
4-Introducao programacao-convertido.pptx4-Introducao programacao-convertido.pptx
4-Introducao programacao-convertido.pptx
 
Aula 01 - Introducao a C (3).pptx
Aula 01 - Introducao a C (3).pptxAula 01 - Introducao a C (3).pptx
Aula 01 - Introducao a C (3).pptx
 
Algoritmos - Paradigmas de Programação
Algoritmos - Paradigmas de ProgramaçãoAlgoritmos - Paradigmas de Programação
Algoritmos - Paradigmas de Programação
 
Lógica de programação pascal
Lógica de programação   pascalLógica de programação   pascal
Lógica de programação pascal
 

Kürzlich hochgeladen

Revolução russa e mexicana. Slides explicativos e atividades
Revolução russa e mexicana. Slides explicativos e atividadesRevolução russa e mexicana. Slides explicativos e atividades
Revolução russa e mexicana. Slides explicativos e atividadesFabianeMartins35
 
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdf
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdfProjeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdf
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdfHELENO FAVACHO
 
Considere a seguinte situação fictícia: Durante uma reunião de equipe em uma...
Considere a seguinte situação fictícia:  Durante uma reunião de equipe em uma...Considere a seguinte situação fictícia:  Durante uma reunião de equipe em uma...
Considere a seguinte situação fictícia: Durante uma reunião de equipe em uma...azulassessoria9
 
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdfBlendaLima1
 
apostila projeto de vida 2 ano ensino médio
apostila projeto de vida 2 ano ensino médioapostila projeto de vida 2 ano ensino médio
apostila projeto de vida 2 ano ensino médiorosenilrucks
 
Araribá slides 9ano.pdf para os alunos do medio
Araribá slides 9ano.pdf para os alunos do medioAraribá slides 9ano.pdf para os alunos do medio
Araribá slides 9ano.pdf para os alunos do medioDomingasMariaRomao
 
Currículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdfCurrículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdfTutor de matemática Ícaro
 
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdfPROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdfHELENO FAVACHO
 
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdf
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdfRecomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdf
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdfFrancisco Márcio Bezerra Oliveira
 
Atividade - Letra da música Esperando na Janela.
Atividade -  Letra da música Esperando na Janela.Atividade -  Letra da música Esperando na Janela.
Atividade - Letra da música Esperando na Janela.Mary Alvarenga
 
Dicionário de Genealogia, autor Gilber Rubim Rangel
Dicionário de Genealogia, autor Gilber Rubim RangelDicionário de Genealogia, autor Gilber Rubim Rangel
Dicionário de Genealogia, autor Gilber Rubim RangelGilber Rubim Rangel
 
Historia da Arte europeia e não só. .pdf
Historia da Arte europeia e não só. .pdfHistoria da Arte europeia e não só. .pdf
Historia da Arte europeia e não só. .pdfEmanuel Pio
 
Urso Castanho, Urso Castanho, o que vês aqui?
Urso Castanho, Urso Castanho, o que vês aqui?Urso Castanho, Urso Castanho, o que vês aqui?
Urso Castanho, Urso Castanho, o que vês aqui?AnabelaGuerreiro7
 
About Vila Galé- Cadeia Empresarial de Hotéis
About Vila Galé- Cadeia Empresarial de HotéisAbout Vila Galé- Cadeia Empresarial de Hotéis
About Vila Galé- Cadeia Empresarial de Hotéisines09cachapa
 
INTERVENÇÃO PARÁ - Formação de Professor
INTERVENÇÃO PARÁ - Formação de ProfessorINTERVENÇÃO PARÁ - Formação de Professor
INTERVENÇÃO PARÁ - Formação de ProfessorEdvanirCosta
 
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdf
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdfENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdf
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdfLeloIurk1
 
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....LuizHenriquedeAlmeid6
 
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptx
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptxSlides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptx
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptxLuizHenriquedeAlmeid6
 
Discurso Direto, Indireto e Indireto Livre.pptx
Discurso Direto, Indireto e Indireto Livre.pptxDiscurso Direto, Indireto e Indireto Livre.pptx
Discurso Direto, Indireto e Indireto Livre.pptxferreirapriscilla84
 
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEM
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEMPRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEM
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEMHELENO FAVACHO
 

Kürzlich hochgeladen (20)

Revolução russa e mexicana. Slides explicativos e atividades
Revolução russa e mexicana. Slides explicativos e atividadesRevolução russa e mexicana. Slides explicativos e atividades
Revolução russa e mexicana. Slides explicativos e atividades
 
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdf
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdfProjeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdf
Projeto de Extensão - ENGENHARIA DE SOFTWARE - BACHARELADO.pdf
 
Considere a seguinte situação fictícia: Durante uma reunião de equipe em uma...
Considere a seguinte situação fictícia:  Durante uma reunião de equipe em uma...Considere a seguinte situação fictícia:  Durante uma reunião de equipe em uma...
Considere a seguinte situação fictícia: Durante uma reunião de equipe em uma...
 
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf
3-Livro-Festa-no-céu-Angela-Lago.pdf-·-versão-1.pdf
 
apostila projeto de vida 2 ano ensino médio
apostila projeto de vida 2 ano ensino médioapostila projeto de vida 2 ano ensino médio
apostila projeto de vida 2 ano ensino médio
 
Araribá slides 9ano.pdf para os alunos do medio
Araribá slides 9ano.pdf para os alunos do medioAraribá slides 9ano.pdf para os alunos do medio
Araribá slides 9ano.pdf para os alunos do medio
 
Currículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdfCurrículo - Ícaro Kleisson - Tutor acadêmico.pdf
Currículo - Ícaro Kleisson - Tutor acadêmico.pdf
 
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdfPROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
PROJETO DE EXTENSÃO - EDUCAÇÃO FÍSICA BACHARELADO.pdf
 
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdf
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdfRecomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdf
Recomposiçao em matematica 1 ano 2024 - ESTUDANTE 1ª série.pdf
 
Atividade - Letra da música Esperando na Janela.
Atividade -  Letra da música Esperando na Janela.Atividade -  Letra da música Esperando na Janela.
Atividade - Letra da música Esperando na Janela.
 
Dicionário de Genealogia, autor Gilber Rubim Rangel
Dicionário de Genealogia, autor Gilber Rubim RangelDicionário de Genealogia, autor Gilber Rubim Rangel
Dicionário de Genealogia, autor Gilber Rubim Rangel
 
Historia da Arte europeia e não só. .pdf
Historia da Arte europeia e não só. .pdfHistoria da Arte europeia e não só. .pdf
Historia da Arte europeia e não só. .pdf
 
Urso Castanho, Urso Castanho, o que vês aqui?
Urso Castanho, Urso Castanho, o que vês aqui?Urso Castanho, Urso Castanho, o que vês aqui?
Urso Castanho, Urso Castanho, o que vês aqui?
 
About Vila Galé- Cadeia Empresarial de Hotéis
About Vila Galé- Cadeia Empresarial de HotéisAbout Vila Galé- Cadeia Empresarial de Hotéis
About Vila Galé- Cadeia Empresarial de Hotéis
 
INTERVENÇÃO PARÁ - Formação de Professor
INTERVENÇÃO PARÁ - Formação de ProfessorINTERVENÇÃO PARÁ - Formação de Professor
INTERVENÇÃO PARÁ - Formação de Professor
 
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdf
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdfENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdf
ENSINO RELIGIOSO 7º ANO INOVE NA ESCOLA.pdf
 
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....
Slides Lição 5, Betel, Ordenança para uma vida de vigilância e oração, 2Tr24....
 
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptx
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptxSlides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptx
Slides Lição 05, Central Gospel, A Grande Tribulação, 1Tr24.pptx
 
Discurso Direto, Indireto e Indireto Livre.pptx
Discurso Direto, Indireto e Indireto Livre.pptxDiscurso Direto, Indireto e Indireto Livre.pptx
Discurso Direto, Indireto e Indireto Livre.pptx
 
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEM
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEMPRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEM
PRÁTICAS PEDAGÓGICAS GESTÃO DA APRENDIZAGEM
 

Cálculo nota final LEI

  • 2. Preliminares •  Apresentação. •  A programação na LEI. •  O que é um computador? •  O que é um programa? •  Linguagens de programação. •  A linguagem de programação C. •  Bibliografia. 18/12/14 Programação Imperativa 2
  • 3. Apresentação •  Aulas teóricas às segundas-feiras, das 9:00 às 10:00 e às quintas-feiras, das 8:30 às 9:30, no anfiteatro 1.8.1, no edifício 8. •  Aulas práticas para várias turmas. •  Professor das teóricas: Pedro Guerreiro. •  Professoras das práticas: Margarida Madeira, Noélia Correia e CristinaVieira. •  Avaliação ao longo do funcionamento e exame final. •  Página na tutoria: http://goo.gl/1B56WO. 18/12/14 Programação Imperativa 3
  • 4. A programação na LEI •  Programação Imperativa. •  Laboratório de Programação. •  Programação Orientada por Objetos. •  Algoritmos e Estruturas de Dados. •  Bases de Dados. •  Computação Gráfica. •  Desenvolvimento de Aplicações para a Web. •  Compiladores. •  Inteligência Artificial. •  ... 18/12/14 Programação Imperativa 4
  • 5. A programação na LEI •  Programação Imperativa. •  Laboratório de Programação. •  Programação Orientada por Objetos. •  Algoritmos e Estruturas de Dados. •  Bases de Dados. •  Computação Gráfica. •  Desenvolvimento de Aplicações para a Web. •  Compiladores. •  Inteligência Artificial. •  ... 18/12/14 Programação Imperativa 5
  • 6. O que é um computador? 18/12/14 Programação Imperativa 6 ENIAC (1946) UNIVAC I (1951)
  • 7. O que é um computador? (2) 18/12/14 Programação Imperativa 7 IBM 360 (1965) DG Eclipse MV/8000 (1980)DECVAX-11/780 (1978) PDP 11/70 (1975)
  • 8. O que é um computador? (3) 18/12/14 Programação Imperativa 8 IBM PC 5150 (12 de Agosto de 1981) Apple Macintosh (24 de Janeiro de 1984)
  • 9. O que é um computador? (4) 18/12/14 Programação Imperativa 9 Computador “torre” Computador “laptop”
  • 10. O que é um computador? (5) 18/12/14 Programação Imperativa 10 Computador “torre” Computador “laptop” Surface Pro 3 e Macbook Air
  • 11. E ainda... 18/12/14 Programação Imperativa 11 Fonte: IEEE Standard Glossary of Computer Hardware Terminology (1994). I'm sorry, Dave. I'm afraid I can't do that.. HAL 9000
  • 12. “Definição” de computador A device that consists of one or more associated processing units and peripheral units, that is controlled by internally stored programs, and that can perform substantial computations, including numerous arithmetic operations, or logic operations, without human intervention during a run. 18/12/14 Programação Imperativa 12 Fonte: IEEE Standard Glossary of Computer Hardware Terminology (1994).
  • 13. O que é um programa? •  Um programa é uma sequência de instruções que um computador executará automatica- mente, para levar a cabo uma determinada tarefa. •  As instruções são executadas sequencial- mente, primeiro a primeira instrução do programa, depois a segunda, e assim por diante até ao fim do programa, exceto no caso das instruções de salto, as quais permitem “saltar” (condicionalmente ou não) para outra instrução, mais à frente ou mais atrás. 18/12/14 Programação Imperativa 13
  • 14. Como são os programas? •  Os programas são texto, isto é, sequências de frases, formadas por palavras, formadas por carateres. •  Os programas são escritos por pessoas ou por outros programas. •  Cada programa é escrito numa linguagem de programação. •  Os compiladores são programas que traduzem um programa escrito numa linguagem para outra linguagem que o computador é capaz de processar mais eficientemente. 18/12/14 Programação Imperativa 14
  • 15. Programação imperativa •  A programação imperativa é um estilo de programação que reflete a ideia fundamental de que as instruções constituem ordens que o computador deve cumprir: read, write, call, stop, wait, add, connect, perform, etc. •  À programação imperativa contrapõe-se a programação funcional, para a qual um programa é a descrição de uma função (no sentido da matemática); executar o programa é avaliar a função para argumentos dados, a fim de obter os correspondentes resultados. 18/12/14 Programação Imperativa 15
  • 16. Linguagens de programação •  Os programas são escritos usando linguagens de programação. •  Cada linguagem de programação é um conjunto de regras definidas inequivocamente num documento de referência. •  Há regras sintáticas (que exprimem as maneiras válidas de escrever programas) e regras semânticas (que exprimem o significado operacional dos programas). •  Há ainda regras de estilo, peculiares de cada organização. 18/12/14 Programação Imperativa 16
  • 18. A linguagem de programação C •  Em Programação Imperativa programaremos em C. •  A linguagem C foi inventada por Dennis Ritchie, nos Laboratórios Bell, em 1972. •  A linguagem C provém da linguagem B, a qual provinha da linguagem BCPL, a qual provinha da linguagem CPL, a qual provinha do Algol 60. •  A linguagem C influenciou diretamente as linguagens C++, Java, Objective C, C# e, mais ou menos diretamente, muitas outras. 18/12/14 Programação Imperativa 18
  • 19. Evolução do C •  1972: invenção do C. •  1989: normalização ANSI C, ou C89. •  1990: normalização ISO C, ou C90, igual à anterior. •  1999: normalização ISO, C99. •  2011: normalização ISO, C11. 18/12/14 Programação Imperativa 19
  • 21. Programação Imperativa Lição n.º 2 Programação com C
  • 22. Programação com C •  Problemas de programação. •  Decomposição funcional. •  Funções em C. •  Funções de teste. 18/12/14 Programação Imperativa 22
  • 23. Problemas de programação •  Tipicamente, a tarefa de um programador é escrever programas para realizar determinadas tarefa, ou para resolver determinados problemas. •  Problema de hoje: escrever um programa C para calcular a nota final em Programação Imperativa, dada a nota da parte prática e a nota do exame. 18/12/14 Programação Imperativa 23
  • 24. Problema da nota final •  A nota final é a média ponderada da nota da parte prática e da nota do exame, com pesos 30% e 70%, respetivamente. •  Mas se a nota do exame for menor que 8.5, a nota final é a nota do exame. •  As notas são expressas na escala de 0 a 20. •  A notas da parte prática e do exame são expressas com uma casa decimal. •  A nota final é expressa na forma de um número inteiro, obtido por arredondamento do resultado dos cálculos. 18/12/14 Programação Imperativa 24
  • 25. Funções identificadas no enunciado •  A função para a média ponderada da nota da parte prática e da nota do exame. •  A função que se ocupa do caso em que a nota do exame é menor do que 8.5. •  A função que arredonda (para o número inteiro mais próximo) o resultado dos cálculos. (Esta é um exemplo das tais funções gerais) 18/12/14 Programação Imperativa 25 Esta é um caso das tais funções gerais.
  • 26. Ambiente de programação •  Programaremos escrevendo os nossos programas num editor de texto e compilando numa janela de comando. •  Teremos numa janela o editor e noutra a janela de comando: 18/12/14 Programação Imperativa 26 A janela de comando está colocada na diretoria onde guardamos os programas.
  • 27. Média ponderada •  Se x representar a nota da prática e y a nota do exame, a média ponderada desses dois valores, usando os pesos 30% e 70%, é dada pela expressão x * 0.3 + y * 0.7. •  As variáveis x e y denotam números reais, com parte decimal. •  Em C, os números reais são representados pelo tipo double. •  A função para a média ponderada terá dois argumentos de tipo double e o resultado também é de tipo double. 18/12/14 Programação Imperativa 27
  • 28. Função weighted_average •  Observe: •  Usamos aqueles nomes lab e exam para deixar claro o significado dos argumentos. 18/12/14 Programação Imperativa 28 double weighted_average(double lab, double exam) { return lab * 0.3 + exam * 0.7; } O compilador dá erro, indicando que o programa não tem uma função main.
  • 29. Função de teste •  Escrevamos uma função de teste para exercitar a função weighted_average: 18/12/14 Programação Imperativa 29 void test_weighted_average(void) { double lb; double ex; scanf("%lf%lf", &lb, &ex); double z = weighted_average(lb, ex); printf("%fn", z); } O compilador continuaria a dar erro, porque continua a faltar a função main.
  • 30. Função main •  A função main chama a função de teste: 18/12/14 Programação Imperativa 30 int main(void) { test_weighted_average(); return 0; } O compilador dá outro erro agora (na verdade, trata-se de um warning...) e sugere que incluamos o “header” <stdio.h>.
  • 31. Programa completo 18/12/14 Programação Imperativa 31 #include <stdio.h> double weighted_average(double lab, double exam) { return lab * 0.3 + exam * 0.7; } void test_weighted_average(void) { double lb; double ex; scanf("%lf%lf", &lb, &ex); double z = weighted_average(lb, ex); printf("%fn", z); } int main(void) { test_weighted_average(); return 0; } Este é o programa completo. Tem uma função de cálculo, uma função de teste e a função main. À cabeça vem a diretiva #include <stdio.h>.
  • 32. Experimentando •  Compilamos e corremos na janela de comando: 18/12/14 Programação Imperativa 32 sources pedro$ gcc -Wall nota_final.c sources pedro$ ./a.out 10 12 11.400000 sources pedro$ ./a.out 15 18 17.100000 sources pedro$ ./a.out 17.2 14.5 15.310000 sources pedro$ ./a.out 14.8 7.1 9.410000 sources pedro$ De cada vez que corremos o programa, só fazemos uma experiência.
  • 33. void test_weighted_average(void) { double lb; double ex; scanf("%lf%lf", &lb, &ex); double z = weighted_average(lb, ex); printf("%fn", z); test_weighted_average(); } Experimentando repetidamente •  É simples: depois de escrever o resultado, chamamos a função de teste, de novo: 18/12/14 Programação Imperativa 33 sources pedro$ ./a.out 12.9 10.0 10.870000 8.5 12.7 11.440000 14.8 12.0 12.840000 ^C sources pedro$ Paramos o programa, interrompendo-o, com ctrl-C.
  • 34. Experimentando repetidamente, melhor •  Em vez de interromper o programa à bruta, com ctrl-C, é melhor deixar o programa seguir quando acabarem os dados. •  Neste caso, o programa seguirá, mas como não há mais nada que fazer, terminará imediatamente. •  O fim dos dados é assinalado com ctrl-Z em Windows e com ctrl-D em Linux/MacOS. •  O ctrl-C é usado para interromper um programa que disparatou ou um programa que chamámos por engano, não para fazer um programa terminar normalmente.18/12/14 Programação Imperativa 34
  • 35. Ciclo de teste •  Observe com muita atenção: 18/12/14 Programação Imperativa 35 void test_weighted_average(void) { double lb; double ex; while (scanf("%lf%lf", &lb, &ex) != EOF) { double z = weighted_average(lb, ex); printf("%fn", z); } } sources pedro$ ./a.out 12 15 14.100000 13.8 19.0 17.440000 10.1 19.9 16.960000 sources pedro$ Eu terei dado ctrl-D para assinalar o fim dos dados, mas o ctrl-D não é ecoado.
  • 36. Função da nota exata •  A média ponderada nem sempre dá a nota; só dá quando a nota do exame é maior ou igual a 8.5 •  Caso contrário, o resultado é a nota do exame. •  Observe: 18/12/14 Programação Imperativa 36 double grade(double lab, double exam) { return exam >= 8.5 ? weighted_average(lab, exam) : exam; } Atenção aos operadores ponto de interrogação e dois pontos!
  • 37. Função de teste para a nota exata •  Para controlo, incluímos também uma chamada à função weighted_average: 18/12/14 Programação Imperativa 37 void test_grade(void) { double lb; double ex; while (scanf("%lf%lf", &lb, &ex) != EOF) { double v = weighted_average(lb, ex); printf("%fn", v); double z = grade(lb, ex); printf("%fn", z); } }
  • 38. A nova função main •  A função main chama agora a nova função de teste. •  A anterior função de teste continua lá, mas comentada: 18/12/14 Programação Imperativa 38 int main(void) { // test_weighted_average(); test_grade(); return 0; } Tipicamente, as funções main são assim: chamam uma de várias funções de teste, estando as outras comentadas, para poderem facilmente ser reativadas, se necessário.
  • 39. Experimentando a nota exata •  Eis uma sessão de experimentação, usando a nova função main: 18/12/14 Programação Imperativa 39 sources pedro$ ./a.out 14 10 11.200000 11.200000 16 8 10.400000 8.000000 16 8.4 10.680000 8.400000 16 8.5 10.750000 10.750000 19 6.2 10.040000 6.200000 sources pedro$ Confirmamos que nos casos em que a nota do exame é menor que 8.5, as duas funções dão resultados diferentes.
  • 40. Conclusão •  Já conseguimos calcular a nota exata, isto é, a nota calculada com toda a precisão. •  Falta calcular a nota final, arredondada. •  Note que as funções presume, que os valores dos argumentos fazem sentido, isto é, que são números reais entre 0.0 e 20.0., expressos com uma casa decimal, mas o programa não controla isso, e calcula cegamente. •  Aliás, se na função de teste fornecermos “lixo”, isto é, sequências de carateres que não constituem números decimais, o programa estoira ingloriamente.18/12/14 Programação Imperativa 40
  • 41. Programação Imperativa Lição n.º 3 Operações aritméticas
  • 42. Programação com C •  Aritmética em C. •  Aritmética int. •  Aritmética double. •  Aritmética mista. •  Funções matemáticas de biblioteca. •  Funções max e min. 18/12/14 Programação Imperativa 42
  • 43. Aritmética em C •  As regras da aritmética do C são semelhantes às da aritmética da matemática, que aprendemos na escola primária. •  Mas há diferenças subtis, que frequentemente nos apanham desprevenidos. •  Primeira observação importante: os números inteiros são representados pelo tipo int, mas o tipo int não representa todos os números inteiros! •  Só representa os número inteiros do intervalo [-2147483648..2147483647]. 18/12/14 Programação Imperativa 43
  • 44. Testando a adição de ints •  Eis um programa com uma função de teste que faz repetidamente a adição de dois números int: 18/12/14 Programação Imperativa 44 #include <stdio.h> void test_addition(void) { int x; int y; while (scanf("%d%d", &x, &y) != EOF) { int z = x + y; printf("%dn", z); } } int main(void) { test_addition(); return 0; } sources pedro$ ./a.out 3 7 10 3000 7000 10000 3000000 7000000 10000000 3000000000 7000000000 1410065408 2000000000 1 2000000001 2000000000 2000000000 -294967296 3000000000 0 -1294967296 Conclusão: quando uma das parcelas ou o resultado sai do intervalo dos int, está tudo estragado.
  • 45. [-2147483648..2147483647] ou [-231..231-1] •  Em C, cada número int ocupa uma palavra de 32 bits. •  A sequência dos valores dos bits corresponde à representação binária do número. •  Logo, com 32 bits, podem ser representados no máximo 232 = 4294967296 números diferentes. •  Metade serão negativos, um é o zero e metade menos um serão positivos. •  Por isso, o intervalo dos números int é [-231..231-1], ou [-2147483648..2147483647]. 18/12/14 Programação Imperativa 45
  • 46. Overflow •  Há overflow de inteiros quando o resultado de um cálculo com números inteiros cai fora do intervalos dos números int. •  Quando há overflow, os cálculos aritméticos ficam errados, irremediavelmente. 18/12/14 Programação Imperativa 46 sources pedro$ ./a.out 2147483647 1 -2147483648 2147483647 10 -2147483639 2147483647 20 -2147483629 -2147483648 -1 2147483647 Repare, 2147483647 + 1 dá -2147483648. É como se o sucessor do maior número fosse o menor número. Analogamente -2147483648 – 1 dá 2147483647, como se o predecessor do menor número fosse o maior número.
  • 47. Operações aritméticas, tipo int •  Adição: x + y •  Subtração: x – y •  Multiplicação: x * y •  Quociente da divisão inteira: x / y •  Resto da divisão inteira: x % y 18/12/14 Programação Imperativa 47 Cuidados: •  Não deixar dar overflow. •  Não deixar o divisor ser zero. Se o divisor for zero, o programa estoira. •  Não usar operandos com valor negativo na operação resto da divisão inteira. Note bem: ambos os operandos, x e y, representam expressões cujo valor é um número int. O resultado, se houver, é um valor de tipo int.
  • 48. Testando as operações aritméticas, int 18/12/14 Programação Imperativa 48 void test_operations_int(void) { int x; int y; while (scanf("%d%d", &x, &y) != EOF) { int z1 = x + y; printf("%dn", z1); int z2 = x - y; printf("%dn", z2); int z3 = x * y; printf("%dn", z3); int z4 = x / y; printf("%dn", z4); int z5 = x % y; printf("%dn", z5); } } sources pedro$ ./a.out 20 7 27 13 140 2 6 33 50 83 -17 1650 0 33 14 3 17 11 42 4 2
  • 49. Operações aritméticas, tipo double •  Adição: x + y •  Subtração: x – y •  Multiplicação: x * y •  Quociente da divisão: x / y 18/12/14 Programação Imperativa 49 Cuidados: •  Não deixar o divisor ser zero. •  Não contar com precisão ilimitada na representação do resultado. Note bem: ambos os operandos, x e y, representam expressões cujo valor é um número double. O resultado, se houver, é de tipo double.
  • 50. Testando as operações aritméticas, double 18/12/14 Programação Imperativa 50 void test_operations_double(void) { double x; double y; while (scanf("%lf%lf", &x, &y) != EOF) { double z1 = x + y; printf("%fn", z1); double z2 = x - y; printf("%fn", z2); double z3 = x * y; printf("%fn", z3); double z4 = x / y; printf("%fn", z4); } } sources pedro$ ./a.out 25.0 4.0 29.000000 21.000000 100.000000 6.250000 14.0 3.0 17.000000 11.000000 42.000000 4.666667 6.125 0.5 6.625000 5.625000 3.062500 12.250000 0.333333 0.5 0.833333 -0.166667 0.166666 0.666666 Note bem: o operador %, resto da divisão inteira, não existe com para números double.
  • 51. Aritmética mista •  Quando numa expressão do tipo x+y, x-y, x*y ou x/y um dos operandos é double e o outro é int, este é “convertido” automaticamente para double e aplicam-se as regras da aritmética de doubles. 18/12/14 Programação Imperativa 51 A conversão inversa, de double para int, é mais delicada, pois, pode fazer-se de várias maneiras: por truncagem (isto é, eliminando a parte decimal), para o inteiro precedente, para o inteiro seguinte, ou para o inteiro mais próximo. Em cada caso, temos de indicar qual pretendemos.
  • 52. Funções matemáticas de biblioteca •  O C traz um pequeno conjunto de funções matemáticas, operando sobre doubles: 18/12/14 Programação Imperativa 52 Função Significado sin(x) Seno de x. cos(x) Cosseno de x. tan(x) Tangente de x. atan2(y, x) Arcotangente de y/x, no intervalo [-π, π]. exp(x) Exponencial de x. log(x) Logaritmo natural de x. pow(x, y) x elevado a y. sqrt(x) Raiz quadrada de x. floor(x) Maior número inteiro menor ou igual a x. ceil(x) Menor número inteiro maior ou igual a x. fabs(x) Valor absoluto de x. Para usar, fazer #include <math.h>.
  • 53. Arredondamento •  No problema da nota, precisamos de arredondar a nota exata, para o inteiro mais próximo, tendo o cuidado de arredondar para cima as meias unidades. •  Eis uma função para fazer esse cálculo, recorrendo à função floor: •  Note que o resultado é um número inteiro representado por um double. 18/12/14 Programação Imperativa 53 double round(double x) { return floor(x+0.5); }
  • 54. Nota final •  A nota final é o arredondamento da nota exata e dever ser expressa no tipo int. •  Devemos pois explicitar a conversão do resul- tado do arredondamento, de double para int. •  Observe: 18/12/14 Programação Imperativa 54 int final_grade(double lab, double exam) { return (int) round(grade(lab, exam)); } Em geral, sendo x uma expressão de tipo double, (int) x é uma expressão de tipo int cujo valor é o valor de x sem a parte decimal.
  • 55. Função de teste para a nota exata •  Acrescentamos o novo cálculo à função test_grade: 18/12/14 Programação Imperativa 55 void test_grade(void) { double lb; double ex; while (scanf("%lf%lf", &lb, &ex) != EOF) { double v = weighted_average(lb, ex); printf("%fn", v); double z = grade(lb, ex); printf("%fn", z); int g = final_grade(lb, ex); printf("%dn", g); } } Em geral, é prudente observar também os resultados intermédios nas funções de teste.
  • 56. Nota de exame necessária •  Problema: para passar com y como nota final, quanto precisa conseguir no exame um aluno cuja nota da prática é x? •  Para começar, precisamos de resolver em ordem a z a inequação 0.3 * x + 0.7 * z >= y. •  Mas o resultado exato não basta, pois a nota do exame é expressa com uma casa decimal. •  E, para mais, se, resolvendo a inequação, z vier menor que 8.5, isso não serve: o valor de z tem de ser pelo menos 8.5. 18/12/14 Programação Imperativa 56
  • 57. Nota necessária exata •  Calculemos primeiro a nota necessária com a precisão possível, sem considerar a questão do 8.5. •  Isso corresponde a resolver a inequação: 18/12/14 Programação Imperativa 57 double exam_exact(double lab, int goal) { return (goal - 0.3 * lab) / 0.7; } Se o resultado for maior que 20.0, isso significa que é impossível passar com a nota desejada.
  • 58. Arredondamento para cima às décimas •  Para arredondar para cima, às unidades, temos a função ceil. •  Como fazer para arredondar às décimas? •  Eis o truque: multiplica-se por 10, arredonda- se às unidades e divide-se por 10: 18/12/14 Programação Imperativa 58 double ceiling_one_decimal(double x) { return ceil(x * 10.0) / 10.0; } E se quiséssemos arredondar para cima às milésimas, como faríamos? E arredondar para cima, às dezenas?
  • 59. Nota necessária com uma casa decimal •  Arredonda-se a nota exata, às décimas, para cima: 18/12/14 Programação Imperativa 59 double exam_one_decimal(double lab, int goal) { return ceiling_one_decimal(exam_exact(lab, goal)); } Temos ainda de considerar o caso em que esta nota é menor que 8.5, pois um tal valor não daria para passar.
  • 60. Funções max e min •  A função max retorna o valor do maior dos seus argumentos. •  A função min, idem, para o menor. •  Como não existem na biblioteca do C, programamo-las nós: 18/12/14 Programação Imperativa 60 double max(double x, double y) { return x >= y ? x : y; } double min(double x, double y) { return x <= y ? x : y; } Estas duas funções são muito úteis, muitas vezes.
  • 61. Nota necessária •  Se a nota exata arredondada for menor do que 8.5 a nota necessária é 8.5; caso contrário, a nota necessária é a nota exata arredondada. •  Por outras palavras: a nota necessária é o máximo entre a nota exata arredondada e 8.5: 18/12/14 Programação Imperativa 61 double exam(double lab, int goal) { return max(exam_one_decimal(lab, goal), 8.5); }
  • 62. sources pedro$ ./a.out 15.0 10 7.857143 7.900000 8.500000 10 8 12.0 15 16.285714 16.300000 16.300000 15 14 12.0 18 20.571429 20.600000 20.600000 18 17 Função de teste •  Na função de teste, observamos os cálculos inter- médios e recalculamos a nota final, e também a nota final se tivéssemos uma décima a menos no exame: 18/12/14 Programação Imperativa 62 void test_exam(void) { double lb; int gl; while (scanf("%lf%d", &lb, &gl) != EOF) { double z1 = exam_exact(lb, gl); printf("%fn", z1); double z2 = exam_one_decimal(lb, gl); printf("%fn", z2); double z = exam(lb, gl); printf("%fn", z); int x1 = grade(lb, z); printf("%dn", x1); int x2 = grade(lb, z - 0.1); printf("%dn", x2); } }
  • 64. Escolhas •  Instrução if-else. •  Constantes simbólicas. •  Declaração de variáveis. •  If-else em cascata. 18/12/14 Programação Imperativa 64
  • 65. Preço de uma chamada em roaming •  Uma chamada feita em roaming num país da zona 1 para um número em Portugal custa 0,234 euros por minuto. •  A taxação é feita ao segundo após o primeiro impulso de 30 segundos. •  Isto quer dizer que se paga por segundo, mas paga-se sempre pelo menos 30 segundos. •  Queremos uma função para dar o preço a pagar em função da duração da chamada. •  Os arredondamentos são feitos em cada chamada, aos milésimos de euro. 18/12/14 Programação Imperativa 65
  • 66. Preço exato, preço certo •  A expressão que dá o preço nos primeiros 30 segundos é constante: corresponde a metade do preço por minuto. •  A expressão que dá o preço após os primeiros 30 segundos é o produto do número de segundos pelo preço de cada segundo. •  O preço de cada segundo é um sexagésimo do preço do minuto (sem arredondamentos). •  Usaremos uma função para o preço exato, calculado com toda a precisão, e uma para o preço certo, arredondado às milésimas. 18/12/14 Programação Imperativa 66
  • 67. Constantes simbólicas •  O preço por minuto é uma constante arbitrária, atualmente 0.234. •  Normalmente, evitamos usar “números mágicos” nos programas. •  Em vez disso, preferimos constantes simbólicas: •  Agora, em vez de 0.234, usaremos, com maior clareza, PRICE_PER_MINUTE. 18/12/14 Programação Imperativa 67 #define PRICE_PER_MINUTE 0.234
  • 68. Preço exato, com expressão condicional •  Em casos simples como este, usamos uma expressão condicional: •  Mas, quando queremos dar mais destaque a cada uma das alternativas, preferimos uma instrução if-else. 18/12/14 Programação Imperativa 68 double roaming_exact(double x) { return x <= 30 ? PRICE_PER_MINUTE / 2 : x * PRICE_PER_MINUTE / 60; }
  • 69. Preço exato, com instrução if-else •  Eis a mesma função, programada à base de uma instrução if-else: 18/12/14 Programação Imperativa 69 double roaming_exact(double x) { double result; if (x <= 30) result = PRICE_PER_MINUTE / 2; else result = x * PRICE_PER_MINUTE / 60; return result; } Quando as funções de cálculo são mais do que o return de uma expressão, usaremos uma variável result para representar o resultado.Assim, é habitual as funções terminarem por return result;.
  • 70. Preço certo, arredondado às milésimas •  A técnica de arredondamento já é conhecida: 18/12/14 Programação Imperativa 70 double round(double x) { return floor(x+0.5); } double round_three_decimals(double x) { return round(x * 1000) / 1000; } double roaming(double x) { return round_three_decimals(roaming_exact(x)); } •  Logo:
  • 71. Função de teste •  Na função de teste, é prudente observar também o cálculo exato: 18/12/14 Programação Imperativa 71 void test_roaming(void) { double seconds; while (scanf("%lf", &seconds) != EOF) { double z1 = roaming_exact(seconds); printf("%fn", z1); double z2 = roaming(seconds); printf("%fn", z2); } } $ ./a.out 30 0.117000 0.117000 15 0.117000 0.117000 31 0.120900 0.121000 35 0.136500 0.137000 100 0.390000 0.390000 103 0.401700 0.402000
  • 72. Declaração de variáveis •  A declaração da variável indica explicitamente o tipo de valores que a variável pode representar. •  Determina também, implicitamente, a “quantidade” de memória necessária para guardar o valor da variável. •  Cada variável tem de ser declarada antes de ser usada. •  Normalmente, declara-se a variável mesmo antes da primeira utilização. •  Se possível, declara-se e inicializa-se no mesmo passo. •  Quase sempre, as variáveis não variam: recebem um valor (por leitura, por cálculo) e guardam esse valor até ao fim. 18/12/14 Programação Imperativa 72
  • 73. Estacionamento no aeroporto de Faro •  Nos parques P1 e P2 do aeroporto de Faro, o preço do estacionamento é dado pela seguinte tabela: 18/12/14 Programação Imperativa 73 Unidades de taxação Preço Primeira unidade de 15 minutos 0.60 Restantes unidades de 15 minutos 0.60 Máximo para o primeiro dia 9.00 Preço hora após 24 horas 1.50 Máximo segundo dia e seguintes 9.00
  • 74. Análise •  Na verdade, há dois casos apenas: o primeiro dia e os restantes dias. •  No primeiro dia paga-se por 60 cêntimos por quarto de hora, mas paga-se no máximo 9 euros. •  No outros dias paga-se 1.50 euros por hora, mas paga-se no máximo 9 euros. •  Como as expressões dos cálculos são complicadas, vamos usar if-else. 18/12/14 Programação Imperativa 74
  • 75. Função parking •  Atenção às contas: 18/12/14 Programação Imperativa 75 double parking(double minutes) { double result; double days_complete = floor(minutes / 1440); if (minutes <= 1440) result = min(9.0, ceil(minutes / 15) * 0.6); else result = 9.0 * days_complete + min(9.0, ceil((minutes - days_complete * 1440) / 60) * 1.5); return result; } Um dia tem 1440 minutos! Neste caso, por natureza, não é preciso arredondar os resultado, pois nunca terão mais do que duas casas decimais. Em rigor, a variável days_complete só é necessária no ramo else. Calculamos logo à cabeça, apenas para aligeirar o código.
  • 76. Reanálise •  Podemos admitir que as regras para calcular o preço do estacionamento não mudam, durante algum tempo, mas que os valores na tabela podem mudar com alguma frequência. •  Por exemplo, o custo dos primeiros 15 minutos pode baixar para 40 cêntimos, para incentivar estadias muito curtas. •  Ou o máximo nos outros dias (depois do primeiro) pode subir para 12 euros, para castigar estadia longas. •  Para incorporar estas mudanças, não bastaria substituir as constantes. 18/12/14 Programação Imperativa 76
  • 77. Nova estratégia •  Programemos “em função” dos valores da tabela, os quais serão representados por constantes simbólicas: •  Vendo bem, agora há três casos: os primeiros 15 minutos; o resto do primeiro dia; os dias seguintes. •  Usaremos instruções if-else em cascata. 18/12/14 Programação Imperativa 77 #define UNIT_1 0.60 #define UNITS_OTHER 0.60 #define MAX_DAY_1 9.00 #define PRICE_PER_HOUR 1.50 #define MAX_DAY_OTHERS 9.00 Haver aqui repetições de valores deve ser uma coincidência passageira.
  • 78. Função parking, melhor e mais complicada •  Veja com atenção: 18/12/14 Programação Imperativa 78 double parking(double x) { double result; double days_complete = floor(x / 1440); if (x <= 15) result = UNIT_1; else if (x <= 1440) result = min(MAX_DAY_1, UNIT_1 + ceil((x - 15) / 15) * UNITS_OTHER); else result = MAX_DAY_1 + MAX_DAY_OTHERS * (days_complete - 1) + min(MAX_DAY_OTHERS, ceil((x - days_complete * 1440) / 60) * PRICE_PER_HOUR); return result; } É mais complicada porque a vida é complicada.
  • 80. Ciclos •  Ciclos for. •  Representação dos números double em memória. •  Afetação. •  Operadores de afetação. •  Formatação de números double. 18/12/14 Programação Imperativa 80
  • 81. Soma geométrica •  Queremos calcular a soma S(x, n) = 1 + x + x2 + ... + xn-1, para um dado x e um dado n. •  Existe uma fórmula: S(x, n) = (xn-1)/(x-1). •  Para x=2, esta fórmula dá 1+2+4+...+2n-1 = 2n-1. •  Para x=1/2, dá 1+1/2+1/4+... = 2. •  Estes dois resultados têm muito interesse computacional. Lembre-se deles! •  Por outro lado: S(x, n) = n == 0 ? 0 : 1 + x * S(x, n-1) 18/12/14 Programação Imperativa 81
  • 82. •  Calculemos à mão S(3, 5): Calculando recursivamente S(3, 5) = 1 + 3 * S(3, 4) = 1 + 3 * (1 + 3 * S(3, 3)) = 1 + 3 * (1 + 3 * (1 + 3 * S(3, 2))) = 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * S(3, 1)))) = 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * S(3, 0))))) = 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * 0)))) = 1 + 3 * (1 + 3 * (1 + 3 * (1 + 3 * 1)))) = 1 + 3 * (1 + 3 * (1 + 3 * 4))) = 1 + 3 * (1 + 3 * 13)) = 1 + 3 * 40 = 121 18/12/14 Programação Imperativa 82 Curioso: calculamos uma soma de potências com expoentes sucessivos, mas não usámos a potenciação.
  • 83. Função para a soma das potências •  Exprimimos em C a definição da função S: 18/12/14 Programação Imperativa 83 double sum_geometric(double x, int n) { return n == 0 ? 0.0 : 1.0 + x * sum_geometric(x, n-1); }
  • 84. Formatos para double no printf •  O especificador “%f” indica que o número aparecerá com parte inteira e parte decimal. Por defeito, a parte decimal vem arredondada com 6 algarismos. •  O especificador “%e” indica que o número aparecerá em notação exponencial, por exemplo, 4.095000e+03. A parte inteira vem está entre1 e 9 e, por defeito, a parte decimal vem arredondada com 6 algarismos. •  O especificador “%g” indica que o número virá na forma mais “apropriada”. 18/12/14 Programação Imperativa 84
  • 85. Testando a soma das potências •  Calculamos e mostramos o resultado usando os três formatos: 18/12/14 Programação Imperativa 85 void test_sum_geometric(void) { double x; int n; while (scanf("%lf%d", &x, &n) != EOF) { double z = sum_geometric(x, n); printf("%fn", z); printf("%en", z); printf("%gn", z); } } $ ./a.out 2 16 65535.000000 6.553500e+04 65535 10 12 111111111111.000000 1.111111e+11 1.11111e+11 0.5 10 1.998047 1.998047e+00 1.99805 0.1 8 1.111111 1.111111e+00 1.11111
  • 86. Fixando a precisão •  Para escolher o número de casas decimais no caso do “%f” e do “%e” ou o número de algarismos usados no “%g”, fixamos a precisão da escrita. •  Por exemplo, mudemos a precisão de 6 (valor por defeito) para 20, nos 3 printf: 18/12/14 Programação Imperativa 86 printf("%.20fn", z); printf("%.20en", z); printf("%.20gn", z);
  • 87. Experimentando com precisão excessiva •  Observe, com precisão 20: 18/12/14 Programação Imperativa 87 $ ./a.out 2 15 32767.00000000000000000000 3.27670000000000000000e+04 32767 10 14 11111111111111.00000000000000000000 1.11111111111110000000e+13 11111111111111 2 40 1099511627775.00000000000000000000 1.09951162777500000000e+12 1099511627775 0.1 15 1.11111111111111005023 1.11111111111111005023e+00 1.1111111111111100502 0.1 30 1.11111111111111116045 1.11111111111111116045e+00 1.1111111111111111605 O último teste mostra que o tipo double não consegue representar números com mais que 17 algarismos.
  • 88. Iteração •  Recorde a definição da função S: S(x, n) = n == 0 ? 0 : 1 + x * S(x, n-1) •  Quer dizer: se R for o valor de S(x, n) então 1+x * R é o valor de S(x, n+1). •  Portanto, começando com R = 0 e fazendo R = 1 + x * R, repetidamente, n vezes, chega-se ao valor de S(x, n): 18/12/14 Programação Imperativa 88 R = 0 R = 1 + x * R // x0 R = 1 + x * R // x0 + x1 R = 1 + x * R // x0 + x1 + x2 ... R = 1 + x * R // x0 + x1 + x2 + ... xn-1
  • 89. Soma das potências, versão iterativa •  Para repetir uma instrução um certo número de vezes, usa-se um ciclo for: 18/12/14 Programação Imperativa 89 double sum_geometric_i(double x, int n) { double result = 0.0; for (int i = 0; i < n; i++) result = 1.0 + x * result; return result; } No fim do passo i, a variável result contém o valor 1+x+x2+...+xi. Portanto, no fim do ciclo for, conterá 1+x+x2+...xn-1, que constitui a soma pretendida. Com este ciclo for, repete-se n vezes.
  • 90. double sum_geometric_t(double x, int n) { double result = 0.0; double term = 1.0; for (int i = 0; i < n; i++) { result += term; term *= x; } return result; } Cada novo resultado parcial é obtido somando o resultado parcial corrente ao termo corrente; cada novo termo é obtido multiplicando o termo corrente por x. Versão iterativa, variante •  Consegue-se o mesmo efeito, enumerando os termos da lista das potências, acumulando a soma no resultado: 18/12/14 Programação Imperativa 90
  • 91. Afetação •  Que significa, em programação x = y? •  A expressão x = y é uma expressão de afetação. •  Em geral, x e y são expressões. •  A expressão x, à esquerda, representa uma variável, isto é, uma posição de memória. •  A expressão y, à direita, representa um valor, que deve pertencer a um tipo que pode ser armazenado na posição de memória representada por x. •  O efeito de x = y é armazenar na posição representada por x o valor representado por y. •  Em geral, o valor de uma variável é o valor que mais recentemente foi armazenado na posição de memória representada por essa variável. 18/12/14 Programação Imperativa 91
  • 92. Operadores de afetação •  O significado dos operadores de afetação +=, −=, *=, /= e %= é “intuitivo”: 18/12/14 Programação Imperativa 92 Utilização Significado x += y x = x + y x −= y x = x − y x *= y x = x * y x /= y x = x / y x %= y x = x % y
  • 93. Variante overkill •  Calcular cada termo da sucessão das potências, usando pow(x, i) seria overkill: 18/12/14 Programação Imperativa 93 double sum_geometric_bad(double x, int n) { double result = 0.0; for (int i = 0; i < n; i++) result += pow(x, i); return result; } Calcular xi por meio de pow(x, i) é esbanjamento de recursos, pois o valor de xi-1 terá sido calculado no passo anterior e para calcular xi, basta multiplicar esse valor por x.
  • 94. void test_sum_geometric_all(void) { double x; int n; while (scanf("%lf%d", &x, &n) != EOF) { double z1 = sum_geometric(x, n); printf("%.16gn", z1); double z2 = sum_geometric_i(x, n); printf("%.16gn", z2); double z3 = sum_geometric_t(x, n); printf("%.16gn", z3); double z4 = sum_geometric_bad(x, n); printf("%.16gn", z4); } } $ ./a.out 2 20 1048575 1048575 1048575 1048575 10 12 111111111111 111111111111 111111111111 111111111111 2 60 1.152921504606847e+18 1.152921504606847e+18 1.152921504606847e+18 1.152921504606847e+18 0.5 30 1.999999998137355 1.999999998137355 1.999999998137355 1.999999998137355 Função de teste •  Eis uma função de teste para as quatro variantes da soma de potências: 18/12/14 Programação Imperativa 94
  • 95. Soma de quadrados •  Baseemo-nos na variante overkill da soma geométrica para programar a soma de quadrados: 18/12/14 Programação Imperativa 95 double sum_squares(int n) { double result = 0.0; for (int i = 0; i < n; i++) result += i * i; return result; } Há uma expressão polinomial que calcula a soma dos quadrados dos n primeiros números naturais (0+1+4+9+...+(n-1)2), pelo que calcular iterativamente é apenas um exercício de programação.A expressão é (n-1)*n*(2*n-1)/6.
  • 96. Soma de quadrados, variante elementar •  É claro que (n+1)2 – n2 = 2*n+1. •  Logo, podemos calcular (n+1)2 a partir de n2, somando 2*n+1: 18/12/14 Programação Imperativa 96 double sum_squares_e(int n) { double result = 0.0; double term = 0.0; for (int i = 0; i < n; i++) { result += term; term += 2 * i + 1; } return result; }
  • 97. Soma de quadrados, mais elementar ainda •  Usando a mesma técnica, sabemos que (2(n+1) + 1) – (2*n+1) = 2. •  Logo, a sequência das diferenças entre quadrados cresce de 2 em 2: 18/12/14 Programação Imperativa 97 double sum_squares_f(int n) { double result = 0.0; double term = 0.0; double delta = 1.0; for (int i = 0; i < n; i++) { result += term; term += delta; delta += 2.0; } return result; } Este é um exercício de programação clássico: calcular a soma dos quadrados nos n primeiros números naturais usando apenas adições.
  • 98. Soma de quadrados, polinomial •  Eis a função que implementa o cálculo direto da soma dos quadrados dos n primeiros números naturais: 18/12/14 Programação Imperativa 98 double sum_squares_p(int n) { return (n - 1.0) * n * (2.0*n - 1.0)/6; } Questão técnica: se tivéssemos programado só com inteiros int, assim: return (n-1) * n * (2*n - 1) / 6; o computador usaria aritmética de inteiros (antes de converter o resultado para double, no fim) e daria overflow logo que o produto no numerador ultrapassasse 231-1, o que acontece para n = 1025.
  • 99. Programação Imperativa Lição n.º 6 Ciclos for e ciclos while
  • 100. Ciclos for e ciclos while •  Utilização dos ciclos for. •  Utilização dos ciclos while. •  Exemplos: soma de sequências, fatorial, função 3x+1, comprimento de números. 18/12/14 Programação Imperativa 100
  • 101. Utilização dos ciclos for •  Usamos um ciclo for para repetir a execução de uma instrução nas situações em sabemos à partida quantas vezes é preciso repetir. •  Tipicamente, número de repetições ou é fixo (o que é raro) ou vem numa variável. •  No seio da instrução repetida, podemos usar a variável de controlo que enumera as repetições. •  Frequentemente, a enumeração começa em 0 ou em 1 e vai de 1em 1. •  A instrução repetida pode ser uma instrução composta, agrupando várias instruções. 18/12/14 Programação Imperativa 101
  • 102. Exemplo: progressão aritmética 18/12/14 Programação Imperativa 102 •  Eis uma função para calcular iterativamente a soma dos n primeiros termos de uma progressão geométrica cujo primeiro termo é x e cuja razão é r: double sum_progression(double x, double r, int n) { double result = 0.0; for (int i = 0; i < n; i++) { result += x; x += r; } return result; } Já sabemos que há uma fórmula fechada para isto, pelo que esta função deve ser considerada apenas um exercício de programação. Note bem: sabemos que queremos somar n termos. Logo, usamos ciclo for.
  • 103. Exemplo: fatorial 18/12/14 Programação Imperativa 103 •  O fatorial de um número inteiro n é o produto de todos os números inteiros entre 1 e n (inclusive): double factorial(int n) { double result = 1.0; for (int i = 1; i <= n; i++) // <= result *= i; return result; } Note bem: aqui a variável de controlo varia entre 1 e n (inclusive); no exemplo da página anterior, variava entre 0 e n-1 (inclusive). Queremos multiplicar n números. Logo, usamos ciclo for. Note bem: a variável de controlo é usada na instrução repetida.
  • 104. Testando o fatorial 18/12/14 Programação Imperativa 104 •  Eis uma função de teste: void test_factorial(void) { int x; while (scanf("%d", &x) != EOF) { double z = factorial(x); printf("%.15gn", z); } } $ ./a.out 1 1 5 120 10 3628800 20 2.43290200817664e+18 30 3.04140932017134e+64 100 9.33262154439441e+157 150 5.71338395644585e+262 170 7.25741561530799e+306 171 inf 300 inf O maior número double é aproximadamente 1.796931e308. Mais que isso, é infinito, representado em C por inf.
  • 105. Problema: soma 1 + 1/2+ 1/4 + 1/8 + ... 18/12/14 Programação Imperativa 105 •  A soma dos inversos das potências de 2 tende para 2. •  Quantos termos é preciso somar para que o resultado seja maior que 2 – x, para um x dado? •  Neste caso, não sabemos à partida quantas vezes temos de repetir a adição r = 1 + r/2 (cf. função sum_geometric, lição 5.) •  (Se soubéssemos, já tínhamos a solução do problema...)
  • 106. Ciclos while •  O ciclo while é usado para repetir uma instrução um número indeterminado de vezes. •  As repetições terminam logo que uma certa condição deixa de se verificar. •  A condição é representada por uma expressão lógica. •  Isto é, as repetições terminam logo que o valor da expressão booleana seja 0, representando falso. •  Não haverá repetições se a expressão valer 0 logo de início. 18/12/14 Programação Imperativa 106
  • 107. Contanto termos da soma aproximada •  Repetimos a instrução r = 1 + r/2, enquanto o valor de r, que representa a soma parcial, for menor que 2 – x, sendo x o tal valor dado. •  Em paralelo, incrementamos um contador de termos adicionados: 18/12/14 Programação Imperativa 107 int count_terms(double x) { int result = 0; double sum = 0.0; while (sum <= 2 - x) { sum = 1 + sum / 2; result++; } return result; } O contador é o resultado da função.
  • 108. Testando a contagem de termos •  Contamos os termos e depois verificamos que a contagem confere, usando a função sum_geometric: 18/12/14 Programação Imperativa 108 void test_count_terms(void) { double x; while (scanf("%lf", &x) != EOF) { int z1 = count_terms(x); printf("%dn", z1); double z2 = sum_geometric(0.5, z1); printf("%.15fn", z2); } } $ ./a.out 0.1 5 1.937500000000000 0.01 8 1.992187500000000 0.001 11 1.999023437500000 0.000001 21 1.999999046325684
  • 109. Problema 3x+1 •  Seja a seguinte função f: •  Qualquer que seja x, a sequência x, f(x), f(f(x)), f(f(f(x))), ..., eventualmente alcança o valor 1, após o que oscila 1, 4, 2, 1, 2, 4, 1, ..., indefinidamente. •  Qual é o comprimento da sequência que começa em x e termina no primeiro 1? 18/12/14 Programação Imperativa 109 f (x) = 3x +1, se x for ímpar x / 2, se não ! " # http://en.wikipedia.org/wiki/Collatz_conjecture http://uva.onlinejudge.org/external/1/100.html
  • 110. Preliminares •  Primeiro, a função f: •  Repare na expressão x % 2 == 1. •  Significa “ser o resto da divisão de x por 2 igual a 1”. •  Por outras palavras,“ser x um número ímpar”. 18/12/14 Programação Imperativa 110 int f(int x) { return x % 2 == 1 ? 3 * x + 1 : x / 2; }
  • 111. Observando a sequência •  Para perceber melhor de que se trata, programemos uma função para mostrar a sequência que começa em x e chega até 1, chamada “ciclo” de x: 18/12/14 Programação Imperativa 111 void show_cycle(int x) { while (x != 1) { printf("%d ", x); x = f(x); } printf("%dn", x); } void test_cycle(void) { int x; while (scanf("%d", &x) != EOF) show_cycle(x); } $ ./a.out 3 3 10 5 16 8 4 2 1 9 9 28 14 7 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 512 512 256 128 64 32 16 8 4 2 1
  • 112. Comprimento do ciclo •  É parecida com show_cycle, substituindo o printf pelo incremento do contador: 18/12/14 Programação Imperativa 112 int cycle_length(int x) { int result = 0; while (x != 1) { result++; x = f(x); } result++; return result; }
  • 113. Refinando a função de teste •  Acrescentamos à função de teste uma chamada da nova função: 18/12/14 Programação Imperativa 113 void test_cycle(void) { int x; while (scanf("%d", &x) != EOF) { show_cycle(x); int z = cycle_length(x); printf("%dn", z); } } $ ./a.out 11 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1 15 40 40 20 10 5 16 8 4 2 1 9
  • 114. Comprimento de um número 18/12/14 Programação Imperativa 114 •  O comprimento de um número inteiro é o número de algarismos da sua representação decimal. •  Exprime-se recursivamente, em termos do comprimento da décima parte do número. •  Mas quando é menor ou igual a 9, o comprimento é 1: int decimal_length(int x) { return x <= 9 ? 1 : 1 + decimal_length(x / 10); }
  • 115. Variante iterativa 18/12/14 Programação Imperativa 115 •  Dividir por 10, sucessivamente, enquanto for maior que 9, e contar as divisões: int decimal_length_i(int x) { int result = 1; while (x > 9) { result++; x /= 10; } return result; }
  • 116. Testando o comprimento do número 18/12/14 Programação Imperativa 116 void test_decimal_length(void) { int x; while (scanf("%d", &x) != EOF) { int z1 = decimal_length(x); printf("%dn", z1); int z2 = decimal_length_i(x); printf("%dn", z2); } } $ ./a.out 6413 4 4 9761826 7 7 5 1 1 0 1 1 3321 4 4 •  Testamos as duas variantes:
  • 118. Arrays •  Generalidades sobre arrays. •  Funções sobre arrays. 18/12/14 Programação Imperativa 118
  • 119. Arrays para quê? •  Até agora, todos os programa que vimos calculam a partir de uns poucos números, desses cálculos resultando um outro número. •  Frequentemente teremos, não uns poucos números, mas sim milhentos, e queremos a partir de eles calcular outros milhentos. •  Mais adiante teremos não só milhentos números, mas também milhentas palavras e, mais geralmente, milhentos objetos de diversos tipos. •  Usaremos arrays para representar nos programas milhentos objetos do mesmo tipo.18/12/14 Programação Imperativa 119
  • 120. Arrays •  Os arrays são sequências de objetos, todos do mesmo tipo, acessíveis para inspeção e para modificação por meio dos respetivos índices. •  Os índices são números inteiros. •  O índice do primeiro elemento é zero; o índice do segundo elemento é 1; o índice do terceiro elemento é 2 e assim por diante. •  Se o array tiver N elementos, o índice do último elemento é N-1. 18/12/14 Programação Imperativa 120 Daqui vem a “tradição” de, em programação, enumerar os objetos a partir de zero.
  • 121. Cultura geral: a palavra “array” •  Em inglês não técnico, o substantivo array é usado para significar um conjunto considerável de objetos de um certo tipo, dispostos de maneira regular: “an array of troops in battle order”; “there is a vast array of literature on the topic”; “a bewildering array of choices”. •  A palavra “array” vem do latim “ad-redare”, um verbo que significa “arranjar”,“colocar em ordem”. •  De “ad-redare” derivam as palavras portuguesas “arriar”,“arrear”,“arreio” (mas não “arredar”). 18/12/14 Programação Imperativa 121 Fontes: WordWeb Dictionary © WordWebSoftware.com. http://www.etymonline.com/index.php?term=array. Dictionary, v 2.2.1© Apple Inc. http://www.ciberduvidas.com/pergunta.php?id=25368. http://pt.wiktionary.org/wiki/arredar#Etimologia. Perante isto, e não só, talvez pudéssemos usar “arreios”, em português...
  • 122. Arrays em memória •  Já sabemos que um objeto de tipo int ocupa em memória uma palavra de 4 bytes. •  Também sabemos que um objeto de tipo double ocupa duas palavras de 4 bytes, isto é, 8 bytes ao todo. •  Um array de int com capacidade para N objetos ocupará 4 * N bytes. •  Esses 4 * N bytes ficam todos de seguida na memória do computador. •  Analogamente para os arrays de double: neste caso serão 8 * N bytes, todos de seguida. 18/12/14 Programação Imperativa 122
  • 123. Capacidade de um array •  A capacidade de um array determina o número de objetos que o array pode conter, em cada momento. •  A capacidade é fixa: uma vez estabelecida, quando o array é criado, não mais mudará. •  Em geral, se um objeto de tipo T ocupa Z bytes, um array de elementos do tipo T com capacidade C ocupará um bloco de memória com C * Z bytes. •  Esse bloco de memória é inamovível. 18/12/14 Programação Imperativa 123
  • 124. Capacidade e tamanho •  Muitas vezes, os arrays são dimensionados por excesso, para poder acondicionar todos os conjuntos de valores que possam surgir. •  Por isso, frequentemente, o número de objetos presente no array é menor que a capacidade. •  O número de elementos presentes no array, em cada momento, é o tamanho do array. •  Se a capacidade for C e o tamanho for N, as C – N posições de maior índice ficam desaproveitadas. •  Durante os cálculos, o tamanho pode mudar; a capacidade não! 18/12/14 Programação Imperativa 124
  • 125. Propriedade fundamental dos arrays Num array, o custo de aceder ao primeiro elemento é igual a custo de aceder ao último ou a qualquer outro elemento. 18/12/14 Programação Imperativa 125
  • 126. Limites dos índices •  Tentar aceder a um array fora dos limites dos índices, isto é, usando um índice menor que zero ou maior ou igual ao tamanho, é um grave erro de programação. •  Excetua-se o caso de querermos acrescentar um elemento ao array. •  Para acrescentar, acedemos ao índice dado pelo valor do tamanho, o qual indica a primeira posição livre, e colocamos nesta posição o valor que queremos acrescentar. •  Logo a seguir, incrementamos o valor do tamanho.18/12/14 Programação Imperativa 126 O qual, não obstante, cometemos muitas vezes.
  • 127. Problema: array dos dígitos. 18/12/14 Programação Imperativa 127 •  Dado um número inteiro não negativo x, queremos programar uma função para construir um array que contenha os dígitos de x, isto é, os números que correspondem aos algarismos usados na representação decimal de x. •  Por exemplo, se o número for 2015, o array ficará com os elementos <5, 1, 0, 2> e o tamanho será 4. Repare: 5, que é o dígito menos significativo, fica na posição de índice 0; 1 fica na posição de índice 1; 0 fica na posição de índice 2; e 2 fica na posição de índice 3.
  • 128. Função digits 18/12/14 Programação Imperativa 128 •  Os argumentos da função serão o número cujos dígitos queremos e o array onde vamos guardar os dígitos calculados. •  A função retorna o tamanho do array, no final das operações. •  Observe a declaração da função, ainda vazia: int digits(int x, int *a) { ... } Repare: int * é o tipo dos arrays de int.
  • 129. Aquecimento: contando os dígitos 18/12/14 Programação Imperativa 129 •  Contar os dígitos de números naturais é mais difícil do que contar os dígitos de números inteiros positivos, porque o zero introduz uma irregularidade. •  De facto, se x estiver no intervalo [10n..10n+1[, x tem n+1 dígitos. •  Ora o zero escapa a estes intervalos e tem de ser tratado à parte. •  Por isso, comecemos pelo caso mais conveniente: contar os dígitos de um número inteiro positivo.
  • 130. Função count_digits_positive 18/12/14 Programação Imperativa 130 •  É uma variante mais simples da função decimal_length_i da lição anterior: int count_digits_positive(int x) { int result = 0; while (x > 0) { result++; x /= 10; } return result; } Note bem: o argumento deve ser positivo. Se for zero (ou negativo) o resultado é inválido.
  • 131. Função count_digits 18/12/14 Programação Imperativa 131 •  Consideramos o caso particular de o argumento ser zero, em que o resultado é 1. •  Fora isso, usamos o caso geral, por meio da função anterior. •  Observe: int count_digits(int x) { int result = 1; if (x > 0) result = count_digits_positive(x); return result; } Aprecie o estilo: evitamos um if-else, inicializando o resultado com o valor por defeito, por assim dizer.
  • 132. Função digits_positive 18/12/14 Programação Imperativa 132 •  Evitemos as chatices do zero, baseando-nos na função count_digits_positive. •  Agora, além de contar, acrescentamos cada dígito ao array: int digits_positive(int x, int *a) { int result = 0; while (x > 0) { a[result++] = x % 10; x /= 10; } return result; } Repare: o resultado representa o tamanho do array, depois da operação.
  • 133. Função digits 18/12/14 Programação Imperativa 133 •  Esta constitui o caso geral: int digits(int x, int *a) { int result = 1; a[0] = 0; if (x > 0) result = digits_positive(x, a); return result; } Deixamos as funções de teste para mais tarde...
  • 134. Outro exemplo: inverter o array 18/12/14 Programação Imperativa 134 •  Queremos construir um array com os elementos de outro array, por ordem inversa. •  Observe: •  O array b é o array de saída; o array a é o array de entrada. •  A função devolve o tamanho do array de saída. int mirror(const int *a, int n, int *b) { for (int i = 0; i < n; i++) b[n-1-i] = a[i]; return n; } O qualificador const indica que o array a não será modificado na função.
  • 136. Arrays e memória •  Lendo e escrevendo arrays. •  Observando os arrays na memória. •  Buffer overflow. 18/12/14 Programação Imperativa 136
  • 137. Escrevendo arrays •  Ocasionalmente queremos observar na consola o conteúdo dos nossos arrays. •  Eis uma função simples que escreve os valores de todos os elementos na mesma linha, cada um antecedido por um espaço, mudando de linha no final: 18/12/14 Programação Imperativa 137 void ints_println_basic(const int *a, int n) { for (int i = 0; i < n; i++) printf(" %d", a[i]); printf("n"); }
  • 138. Testando a função digits •  Usemos a função ints_println_basic para testar na consola a função digits: 18/12/14 Programação Imperativa 138 void test_digits(void) { int x; while (scanf("%d", &x) != EOF) { int a[10]; int n = digits(x, a); ints_println_basic(a, n); } } $ ./a.out 2015 5 1 0 2 300 0 0 3 7 7 2147483647 7 4 6 3 8 4 7 4 1 2 0 0 Parece que o array está ao contrário, mas não está: o primeiro elemento, de índice 0, corresponde ao algarismo das unidades, etc.
  • 139. Declaração de arrays •  Repare bem: ao declarar um array, indicamos a sua capacidade: •  A capacidade determina a quantidade de memória reservada para o array. •  Quando usamos um array como argumento de uma função, não indicamos a capacidade, mas tipicamente passamos em argumento também o tamanho do array, nos arrays de entrada, ou devolvemos o tamanho calculado, nos arrays de saída. 18/12/14 Programação Imperativa 139 int a[10];
  • 140. Preciosismo na função ints_println_basic •  Aquele espaço no início da linha, antes do primeiro valor, é deveras irritante. •  De facto, o que queremos é separar cada dois valores consecutivos por um espaço e não colocar um espaço antes de cada valor. •  Ou seja, queremos um espaço antes de cada valor, exceto antes do primeiro. •  Logo, o primeiro elemento do array tem de ser tratado à parte. 18/12/14 Programação Imperativa 140 2015 5 1 0 2
  • 141. Função de escrita, básica •  Note que se o array estiver vazio, isto é, se o tamanho for zero, a função apenas muda de linha. 18/12/14 Programação Imperativa 141 void ints_println_basic(const int *a, int n) { if (n > 0) { printf("%d", a[0]); for (int i = 1; i < n; i++) // i = 1 printf(" %d", a[i]); } printf("n"); }
  • 142. Testando de novo •  Fazemos como antes, mas em cada caso invertemos o array, com a função mirror, para experimentar: 18/12/14 Programação Imperativa 142 void test_mirror(void) { int x; while (scanf("%d", &x) != EOF) { int a[10]; int n = digits(x, a); ints_println_basic(a, n); int b[10]; int m = mirror(a, n, b); ints_println_basic(b, m); } } $ ./a.out 1945 5 4 9 1 1 9 4 5 20141016 6 1 0 1 4 1 0 2 2 0 1 4 1 0 1 6 5 5 5
  • 143. Lendo arrays •  Por hipótese, queremos ler da consola uma sequência de números, até ao fim dos dados. •  Cada número lido é acrescentado ao array: 18/12/14 Programação Imperativa 143 int ints_get(int *a) { int result = 0; int x; while (scanf("%d", &x) != EOF) a[result++] = x; return result; }
  • 144. Testando a leitura de arrays •  Lemos um array, invertemo-lo para outro, com a função mirror, e mostramos o array lido e o array invertido: 18/12/14 Programação Imperativa 144 void test_ints_get() { int a[1000]; int n = ints_get(a); int b[1000]; int m = mirror(a, n, b); ints_println_basic(a, n); ints_println_basic(b, m); } O valor 1000 para a capacidade é um valor arbitrário, apenas para teste. $ ./a.out 1 2 3 4 5 1 2 3 4 5 5 4 3 2 1 $ ./a.out 12 34 45 67 89 97 86 75 64 53 42 31 54 63 72 81 90 12 34 45 67 89 97 86 75 64 53 42 31 54 63 72 81 90 90 81 72 63 54 31 42 53 64 75 86 97 89 67 45 34 12 Repare que esta função de teste não é iterativa. A iteração existente está na leitura dos dados.
  • 145. Buffer overflow •  Se lermos números demais, ultrapassando a capacidade do array, causamos buffer overflow. •  A memória fica corrompida e, a partir daí, o programa está comprometido. •  Experimentemos com uma função de teste simples: 18/12/14 Programação Imperativa 145 void test_buffer_overflow_1() { int a[4]; int n = ints_get(a); ints_println_basic(a, n); } $ ./a.out 1 3 5 7 1 3 5 7 OK $ ./a.out 1 3 5 7 9 11 13 15 1 3 5 7 9 11 13 15 Abort trap: 6 $ int main(void) { test_buffer_overflow_1(); printf("OKn"); return 0; } No segundo teste, em que há buffer overflow, o programa estoira à saída da função de teste, antes de executar a instrução printf na função main.
  • 146. Observando a memória do programa •  Usando oVisual Studio em Windows ou o Xcode em MacOS, podemos parar o programa onde quisermos e observar a memória. •  Aqui, parámos antes da leitura: 18/12/14 Programação Imperativa 146 A azul, a memória da variável n; a verde, a memória do array a. Nesta altura, a memória contém “lixo”.
  • 147. Preenchimento da memória •  Logo a seguir à leitura, os valores de n e as posições lidas do array a ficam preenchidas: 18/12/14 Programação Imperativa 147 O conteúdo da memória está representado em notação hexadecimal, da direita para a esquerda: 04 00 00 00 é, na verdade, 00 00 00 04, ou seja 4, em notação decimal. Isto é a consola, no Xcode: os valores lidos foram 1, 3, 5 e 7.
  • 148. Exemplo com dois arrays •  Consideremos a seguinte função de teste, com dois arrays: 18/12/14 Programação Imperativa 148 void test_buffer_overflow_2() { int a[10]; int n = ints_get(a); int b[4]; int m = mirror(a, n, b); ints_println_basic(a, n); ints_println_basic(b, m); } $ ./a.out 10 20 30 10 20 30 30 20 10 $ ./a.out 5 10 15 20 5 10 15 20 20 15 10 5 $ ./a.out 7 14 21 28 35 42 49 56 7 14 14 7 35 42 49 56 56 49 42 35 7 14 14 7 No terceiro teste, o array b transbordou e corrompeu o array a. Mas note que o programa terminou normalmente.
  • 149. Corrupção da memória (1) •  Eis o estado da memória, após a leitura de 8 números: 7, 14, 21, 28, 35, 42, 49, 56: 18/12/14 Programação Imperativa 149 A azul, m, ainda com “lixo”; a cor de rosa, n, com valor 8; a verde, b, ainda não inicializado; a amarelo, a, com 8 posições preenchidas.
  • 150. Corrupção da memória (2) •  Eis o estado da memória, após a chamada da função mirror e das duas escritas: 18/12/14 Programação Imperativa 150 Recapitulemos as operações da função mirror. Primeiro b[7] = a[0].Mas b[7] coincide com a[3]. Logo a[3] fica com 7. Depois b[6] = a[1]. Mas b[6] coincide com a[2]. Logo a[2] fica com 14. Quer dizer, por via destas duas operações, o conteúdo do array a mudou, mas não devia ter mudado. Depois b[5] = a[2]. Mas b[5] coincide com a[1]. Etc.
  • 151. Erro de execução •  Se o buffer overflow ocorre dentro da zona de memória reservada para o conjunto das variáveis da função, a memória fica corrompida, mas o programa continua,“alegremente”. •  Mas se o buffer overflow sai dessa zona, então ocorre um erro de execução: •  O erro ocorre à saída da função, porque a informação de retorno está estragada. 18/12/14 Programação Imperativa 151 $ ./a.out 10 20 30 40 50 60 70 80 90 100 110 120 10 20 30 40 40 30 20 10 90 100 110 120 120 110 100 90 10 20 30 40 40 30 20 10 Abort trap: 6 Em Windows, apareceu-me uma janela avisando: “a.exe has stopped working, etc.”
  • 152. O C é assim mesmo •  Em linguagens mais modernas, ocorre um erro de execução “index out of bounds” quando tentamos aceder a um array fora do intervalo dos índices. •  Em C não. •  Em C, um array é apenas um pedaço de memória: o programa em execução sabe onde começa cada array (na posição de índice 0), mas não sabe onde acaba. •  A capacidade de cada array não está registada na memória, em lado nenhum. •  Por isso, podemos ir pela memória fora, ultrapas- sando o fim dos arrays, sem controlo. •  O C é assim mesmo. •  Por isso é que nós gostamos dele. 18/12/14 Programação Imperativa 152
  • 154. Estatísticas •  Soma, média, máximo, mínimo de arrays. •  Redirigindo o input. •  Argumento do máximo, do mínimo. •  Testes unitários. 18/12/14 Programação Imperativa 154
  • 155. Ler e escrever arrays de doubles •  As funções para ler e escrever arrays de double são parecidas com as usadas com arrays de int: 18/12/14 Programação Imperativa 155 int doubles_get(double *a) { int result = 0; double x; while (scanf("%lf", &x) != EOF) a[result++] = x; return result; } void doubles_println_basic(const double *a, int n) { if (n > 0) { printf("%g", a[0]); for (int i = 1; i < n; i++) // i = 1 printf(" %g", a[i]); } printf("n"); }
  • 156. Testando a leitura e a escrita •  Eis uma função de teste: 18/12/14 Programação Imperativa 156 void test_doubles_get() { double a[1000]; int n = doubles_get(a); doubles_println_basic(a, n); } $ ./a.out 6.3 0 56 -120.5 8 1.333 34.1 1.41 3.1415926 -999 6.3 0 56 -120.5 8 1.333 34.1 1.41 3.14159 -999 $ ./a.out A 1 3 5 8.111 9 10.7 1 3 5 8.111 9 10.7 $ No primeiro teste, os números para o array foram escritos em duas linhas.
  • 157. Contagem •  Problema: quantos elementos do array têm um valor dado? 18/12/14 Programação Imperativa 157 int doubles_count(const double *a, int n, int x) { int result = 0; for (int i = 0; i < n; i++) if (a[i] == x) result++; return result; } Repare na conjugação for-if: só alguns elementos do array interessam, por assim dizer.
  • 158. Testando a contagem •  Aceitamos o valor de referência, depois o array até ao fim dos dados, operamos e mostramos o resultado: 18/12/14 Programação Imperativa 158 void test_doubles_count(void) { double x; scanf("%lf", &x); double a[1000]; int n = doubles_get(a); int z = doubles_count(a, n, x); printf("%dn", z); } $ ./a.out 4 7 4 9 4 4 2 0 4 1 4 8 5 4 9 9 6 $ ./a.out 7 9 1 2 71 17 1 5 0
  • 159. Problema prático •  Quantos dias choveu em Faro este ano? •  Temos o registo da precipitação em Faro, em cada dia, desde 1 de Janeiro até 11 de Outubro, deste ano. •  O ficheiro tem um número por linha, representando a precipitação, dia a dia. •  Eis uma função que realiza a tarefa pedida: 18/12/14 Programação Imperativa 159 void task_rainy_days(void) { double p[50000]; // not more than 50000 int n = doubles_get(p); int z = n - doubles_count(p, n, 0.0); printf("%dn", z); } 0.2 0 0.2 4 0 0 0 0 0 0 0 0 6 0.2 0 0.5 16.7 1 4 0 0 7.8 …
  • 160. Correndo na consola •  Podemos introduzir os dados com copy-paste, a partir do ficheiro: 18/12/14 Programação Imperativa 160 $ ./a.out 0.2 0 0.2 4 0 2 7.8 0 0 … 0 0 0 2 14.9 0 44 Estes números todos (mais de 250) terão sido metidos na consola com copy-paste, o que não é muito prático. O programa escreveu 44, que é o número de dia de chuva em Faro, este ano, até agora.
  • 161. Redirigindo o input •  É mais prático redirigir o input, instruindo na linha de comando o programa para ir buscar os dados ao ficheiro, em vez de os aceitar a partir do teclado. •  Observe: 18/12/14 Programação Imperativa 161 $ ./a.out < chuva_faro.txt 44 Estamos a supor que o ficheiro chuva_faro.txt, que contém os dados, está da diretoria corrente, a mesma que contém o ficheiro executável a.out. Mas isso nem sempre é prático.
  • 162. Diretorias de dados •  Guardaremos os dados de cada problema numa diretoria própria, dentro da diretoria work, a qual está ao lado da diretoria sources: 18/12/14 Programação Imperativa 162 •  Assim, tipicamente, para correr programas, colocamo-nos na diretoria de dados e chamamos o programa que está na diretoria sources: $ ../../sources/a.out < chuva_faro.txt 44 Quer dizer, a partir de agora trabalharemos com duas janelas de comando: uma para compilar, na diretoria sources; e outra para correr programas, na diretoria de dados.
  • 163. Soma do array •  A soma é um indicador estatístico importante: 18/12/14 Programação Imperativa 163 double doubles_sum(const double *a, int n) { double result = 0; for (int i = 0; i < n; i++) result += a[i]; return result; } void test_doubles_sum(void) { double a[1000]; int n = doubles_get(a); int z = doubles_sum(a, n); printf("%dn", z); } $ ./a.out 5 7 3 1 16 $ ./a.out 1 1 1 1 1 1 1 2 2 2 2 2 2 2 21 Em cada passo, o valor de result é a soma “parcial”, isto é, a soma de todos os valores observado “até agora”.
  • 164. Testes unitários •  Em vez de correr os testes interativamente, na janela de comando, por vezes é mais prático e mais seguro ter um conjunto de testes fixos, programados em funções de teste unitário: 18/12/14 Programação Imperativa 164 void unit_test_doubles_sum(void) { double a1[8] = {6,7,1,8, 9,3,3,5}; assert(doubles_sum(a1, 8) == 42); assert(doubles_sum(a1, 4) == 22); assert(doubles_sum(a1, 2) == 13); assert(doubles_sum(a1, 1) == 6); assert(doubles_sum(a1, 0) == 0); double a2[10] = {1,5,9,13, 17,21,25,29, 33,37}; assert(doubles_sum(a2, 10) == 190); assert(doubles_sum(a2, 5) == 45); } Ao primeiro assert que falhe, o programa termina, com uma mensagem de erro que indica a linha culpada.
  • 165. Teste unitário da função doubles_count •  Cada função importante virá acompanhada do seu teste unitário. •  Eis o teste unitário da função doubles_count: 18/12/14 Programação Imperativa 165 void unit_test_doubles_count(void) { double a1[16] = {6,7,1,8, 9,3,3,5, 6,7,3,9, 6,1,1,1}; assert(doubles_count(a1, 16, 1) == 4); assert(doubles_count(a1, 16, 9) == 2); assert(doubles_count(a1, 16, 2) == 0); assert(doubles_count(a1, 8, 1) == 1); assert(doubles_count(a1, 8, 2) == 0); assert(doubles_count(a1, 0, 6) == 0); } É verdade: os testes ocupam mais código do que as funções de cálculo propriamente ditas.
  • 166. Correndo os testes unitários •  Reunimos todos os testes unitário numa função unit_tests, que será chamada na função main, logo no início: 18/12/14 Programação Imperativa 166 void unit_tests(void) { unit_test_doubles_count(); unit_test_doubles_sum(); // ... } int main(void) { unit_tests(); // ... return 0; } Assim, de cada vez que corremos o programa, corremos os testes todos, automaticamente. Se houver azar, veremos logo.
  • 167. Média •  A partir da soma, calcula-se a média: 18/12/14 Programação Imperativa 167 double doubles_mean(const double *a, int n) { return doubles_sum(a, n) / n; }
  • 168. Máximo de um array •  Calcular o valor do maior elemento presente no array é um problema clássico. •  Se o array não for vazio, podemos programar assim: •  E se o array puder ser vazio? 18/12/14 Programação Imperativa 168 double doubles_max_non_empty(const double *a, int n) { assert(n > 0); double result = a[0]; for (int i = 1; i < n; i++) // i = 1 if (result < a[i]) result = a[i]; return result; } Em cada passo, o valor de result é o máximo “parcial”, isto é, o maior valor observado “até agora”. Se n for zero, a asserção falha e o program estoira.
  • 169. Máximo de um array, caso geral •  Convencionamos que o máximo de um array vazio é menos infinito. •  Observe: 18/12/14 Programação Imperativa 169 double doubles_max(const double *a, int n) { double result = -INFINITY; for (int i = 0; i < n; i++) if (result < a[i]) result = a[i]; return result; }
  • 170. Testes unitários da função doubles_max 18/12/14 Programação Imperativa 170 void unit_test_doubles_max(void) { double a1[16] = {6,7,3,8, 9,3,3,5, 6,7,3,9, 6,1,8,3}; assert(doubles_max(a1, 16) == 9); assert(doubles_max(a1, 4) == 8); assert(doubles_max(a1, 1) == 6); double a2[10] = {32,67,81,23, 27,12,90,13, 75,13}; assert(doubles_max(a2, 10) == 90); assert(doubles_max(a2, 6) == 81); assert(isinf(doubles_max(a2, 0))); double a3[5] = {7e15,3e18,2e14,4e22,3e13}; assert(doubles_max(a3, 5) == 4e22); double a4[5] = {7e-153,3e-185,2e-140,9e-225,3e-213}; assert(doubles_max(a4, 5) == 2e-140); double a5[5] = {-7e200,-3e185,-2e240,-7e225,-3e280}; assert(doubles_max(a5, 5) == -3e185); } Os testes unitário são longos e chatos, mas utilíssimos!
  • 171. Argumento do máximo •  Por vezes, não nos interessa o máximo, mas sim a sua posição no array: 18/12/14 Programação Imperativa 171 int doubles_argmax(const double *a, int n) { assert(n > 0); int result = 0; double m = a[0]; for (int i = 1; i < n; i++) // i = 1 if (m < a[i]) { result = i; m = a[result]; } return result; } Atenção: esta função só pode ser usada com arrays não vazios.
  • 172. Habilidades com o C •  As duas instruções dentro do if podem juntar- se numa só: 18/12/14 Programação Imperativa 172 int doubles_argmax(const double *a, int n) { assert(n > 0); int result = 0; double m = a[0]; for (int i = 1; i < n; i++) // i = 1 if (m < a[i]) m = a[result = i]; return result; } OK, mas não abusemos...
  • 173. Mínimo, argumento do mínimo •  São parecidas com as anteriores: 18/12/14 Programação Imperativa 173 double doubles_min(const double *a, int n) { double result = +INFINITY; for (int i = 0; i < n; i++) if (result > a[i]) result = a[i]; return result; } int doubles_argmin(const double *a, int n) { assert(n > 0); int result = 0; double m = a[0]; for (int i = 1; i < n; i++) // i = 1 if (m > a[i]) m = a[result = i]; return result; }
  • 174. Testes unitários para todas •  Todas estas funções têm o seu teste unitário: 18/12/14 Programação Imperativa 174 void unit_tests(void) { unit_test_doubles_count(); unit_test_doubles_sum(); unit_test_doubles_max(); unit_test_doubles_min(); unit_test_doubles_argmax(); unit_test_doubles_argmin(); } int main(void) { unit_tests(); // ... return 0; }
  • 176. Buscas •  Buscas lineares em arrays. •  Operadores lógicos. •  Igualdade de arrays. •  Reabrindo a consola. 18/12/14 Programação Imperativa 176
  • 177. Problema da busca •  Existe no array um elemento com um dado valor? •  A resposta é sim ou não, representados em C por 1 e 0, respetivamente. •  Alternativamente: qual a posição no array do primeiro elemento com um dado valor? •  Neste caso, a resposta é um número inteiro. •  E que resposta devemos dar quando não existe nenhum elemento com o valor dado? •  Por convenção, quando o valor procurado não existe, a resposta inequívoca é -1. 18/12/14 Programação Imperativa 177
  • 178. Teste unitário •  Eis o protótipo da função de busca em arrays de int: •  Podemos escrever já o teste unitário: 18/12/14 Programação Imperativa 178 int ints_find(const int *a, int n, int x); void unit_test_ints_find(void) { int a[8] = {6,2,9,1, 4,2,7,5}; assert(ints_find(a, 8, 9) == 2); assert(ints_find(a, 8, 5) == 7); assert(ints_find(a, 8, 6) == 0); assert(ints_find(a, 8, 3) == -1); assert(ints_find(a, 4, 9) == 2); assert(ints_find(a, 4, 5) == -1); assert(ints_find(a, 4, 6) == 0); assert(ints_find(a, 8, 3) == -1); assert(ints_find(a, 1, 9) == -1); assert(ints_find(a, 1, 6) == 0); assert(ints_find(a, 0, 6) == -1); assert(ints_find(a, 0, 4) == -1); } Note que o teste unitário serve também para esclarecer o significado da função. A função devolve o índice da primeira ocorrência do valor x no array a (cujo tamanho é n) ou -1, se não houver.
  • 179. Função ints_find •  Esta função é exemplar: 18/12/14 Programação Imperativa 179 int ints_find(const int *a, int n, int x) { for (int i = 0; i < n; i++) if (a[i] == x) return i; return -1; } Repare bem: dois returns, o primeiro dentro do ciclo, o seguindo após o ciclo. Esta é a única função em que usamos esta técnica.
  • 180. Aplicação: validação de números de aluno •  Queremos um programa para validar interativamente números de aluno. •  O problema tem acesso a um ficheiro com os números de todos os alunos inscritos. •  Se o número for válido, o programa escreve 1; se não, escreve 0. •  O ficheiro é lido por redireção do input. 18/12/14 Programação Imperativa 180 ... 44928 48075 50816 51732 52395 50076 45934 52263 50110 52875 51493 51990 44949 48272 52722 49728 52260 52319 51749 ...
  • 181. Questão prévia •  Se o input é redirigido para o ficheiro, como podemos depois usar a janela de comando para interagir com o programa? •  Ora bem: temos de “reabrir a consola”! •  Isso faz-se assim em Windows: •  E assim em Unix: 18/12/14 Programação Imperativa 181 freopen("/dev/tty", "r", stdin); freopen("CON", "r", stdin);
  • 182. Tarefa de validação •  Observe: 18/12/14 Programação Imperativa 182 void task_validate_student(void) { int a[500]; int n = ints_get(a); freopen("/dev/tty", "r", stdin); // Unix // freopen("CON", "r", stdin); // Windows int x; while (scanf("%d", &x) != EOF) { int z = ints_find(a, n, x); printf("%dn", z != -1); } } Repare: a expressão z != -1 vale 1 se z for diferente de -1 e vale 0 se z for igual a -1, tal como convém.
  • 183. Correndo na consola •  O comando que invoca o programa realiza a redireção do input: 18/12/14 Programação Imperativa 183 $ ../../sources/a.out < inscritos.txt 33445 0 45634 0 52092 1 52080 1 50000 0 41895 1 52230 0 40758 1 $ O ficheiro inscritos.txt está arrumado de acordo com as nossas convenções, numa subdiretoria da diretoria work, a qual está a par da diretoria sources, onde reside o executável a.out.
  • 184. Busca do fim para o princípio •  Por vezes, queremos a última ocorrência. •  Nesse caso, procuramos do fim para o princípio: 18/12/14 Programação Imperativa 184 int ints_find_last(const int *a, int n, int x) { int result = n-1; while (result >= 0 && a[result] != x) result--; return result; } Note que neste caso não há interesse em usar o esquema dos dois returns, pois a variável result tomará o valor -1 “naturalmente”, quando a busca falhar.
  • 185. Operadores lógicos 18/12/14 Programação Imperativa 185 && conjunção || disjunção ! negação
  • 186. Variante: obter todas as ocorrências •  Se queremos não a primeira ocorrência mas sim todas as ocorrências, precisamos de um array: 18/12/14 Programação Imperativa 186 int ints_find_all(const int *a, int n, int x, int *b) { int result = 0; for (int i = 0; i < n; i++) if (a[i] == x) b[result++] = i; return result; } Note que b é um array de índices. Ficará vazio se x não ocorrer em a.
  • 187. Array das primeiras ocorrências •  Em geral, cada valor pode ocorrer várias vezes. •  Queremos agora calcular o array das primeiras ocorrências de cada valor, dito a “essência” do array: 18/12/14 Programação Imperativa 187 int ints_nub(const int *a, int n, int *b) { int result = 0; for (int i = 0; i < n; i++) if (ints_find(b, result, a[i]) == -1) b[result++] = a[i]; return result; } Palavras para quê?
  • 188. Testando ints_nub •  Eis uma função de teste, como habitualmente: 18/12/14 Programação Imperativa 188 void test_ints_nub(void) { int a[1000]; int n = ints_get(a); int b[1000]; int m = ints_nub(a, n, b); ints_println_basic(b, m); } $ ./a.out 5 7 5 5 2 7 3 2 2 3 3 1 1 7 5 7 2 3 1 $ ./a.out 3 3 3 3 3 4 4 4 3 3 3 5 5 5 4 4 4 1 1 3 4 5 1 $
  • 189. Aplicação: quantos alunos na aula prática? •  Queremos saber quantos alunos vieram à aula, com base no registo das submissões ao Mooshak. •  De cada submissão, retiramos o número de aluno e guardamos os números num ficheiro. •  Lemos para um array e fazemos ints_nub: 18/12/14 Programação Imperativa 189 void task_students_in_lab(void) { int a[500]; int n = ints_get(a); int b[500]; int m = ints_nub(a, n, b); printf("%dn", m); } $ ../../sources/a.out < submissoes.txt 25 $ ... 49863 49863 51767 52727 51767 51767 52495 51767 51767 49863 49863 49863 45934 51493 45934 51493 50372 50372 49863 50372 50372 ...
  • 190. Igualdade de arrays •  Para verificar se dois arrays a e b são iguais, isto é, se têm os mesmos elementos, pela mesma ordem, não basta escrever a == b. •  É preciso programar uma função ad hoc: 18/12/14 Programação Imperativa 190 int ints_equal_arrays( const int *a, const int n, const int *b, int m) { int result = n == m; int i = 0; while (result && i < n) if (a[i] != b[i]) result = 0; else i++; return result; } Se os tamanhos forem diferentes, os arrays não são iguais. Sendo os tamanhos iguais, procura-se o primeiro par de elementos diferentes. Logo que se encontre, sabe-se que os arrays não são iguais. Não encontrando, os arrays são iguais. Ao longo do ciclo, o valor da variável result significa “os arrays são iguais até agora”, por assim dizer.
  • 191. Testando a igualdade de arrays •  Se lermos os arrays com ints_get, temos de reabrir a consola: 18/12/14 Programação Imperativa 191 void test_ints_equal_arrays(void) { int a[1000]; int n = ints_get(a); freopen("/dev/tty", "r", stdin); // Unix // freopen("CON", "r", stdin); // Windows int b[1000]; int m = ints_get(b); int z = ints_equal_arrays(a, n, b, m); printf("%dn", z); }
  • 192. Testes unitários com igualdade de arrays •  Observe, por exemplo: 18/12/14 Programação Imperativa 192 void unit_test_ints_nub(void) { int a1[12] = {6,2,6,9, 4,2,9,9, 9,2,1,2}; int b1[12]; int m1 = ints_nub(a1, 12, b1); int z1[5] = {6,2,9,4,1}; assert(ints_equal_arrays(b1, m1, z1, 5)); int a2[6] = {1,2,3,3,2,1}; int b2[6]; int m2 = ints_nub(a2, 6, b2); int z2[3] = {1,2,3}; assert(ints_equal_arrays(b2, m2, z2, 3)); int a3[5] = {8,8,8,8,8}; int b3[5]; int m3 = ints_nub(a3, 5, b3); assert(m3 == 1 && b3[0] == 8); }
  • 194. Subarrays •  Subarrays em C. •  Grupos. •  Remoção de duplicados. 18/12/14 Programação Imperativa 194
  • 195. Subarrays •  Atenção: não há subarrays em C. •  O que há é uma maneira de nos referirmos, nas funções, a uma parte de um array. •  Na verdade, já observámos isso, por exemplo nas funções de teste unitário: 18/12/14 Programação Imperativa 195 void unit_test_doubles_sum(void) { double a1[8] = {6,7,1,8, 9,3,3,5}; assert(doubles_sum(a1, 8) == 42); assert(doubles_sum(a1, 4) == 22); assert(doubles_sum(a1, 2) == 13); assert(doubles_sum(a1, 1) == 6); assert(doubles_sum(a1, 0) == 0); } O array a tem 8 elementos, mas ao somar os 4 primeiros elementos é como se estivéssemos a somar todos os elementos do subarray inicial de a, com 4 elementos.
  • 196. Subarrays gerais •  Também podemos ter subarrays não iniciais, como ilustram as seguintes funções de teste unitário: 18/12/14 Programação Imperativa 196 void unit_test_subarrays_sum(void) { double a[8] = {4,9,4,4, 5,2,7,5}; assert(doubles_sum(a, 8) == 40); assert(doubles_sum(a, 5) == 26); assert(doubles_sum(a+3, 5) == 23); assert(doubles_sum(a+2, 4) == 15); } void unit_test_subarrays_max(void) { double a[8] = {4,9,4,4, 5,2,7,5}; assert(doubles_max(a, 8) == 9); assert(doubles_max(a, 5) == 9); assert(doubles_max(a+3, 5) == 7); assert(doubles_max(a+2, 4) == 5); } Por exemplo, a expressão doubles_sum(a+3, 5) representa a soma dos valores de a[3], a[4], a[5], a[6] e a[7]. Por exemplo, a expressão doubles_max(a+2, 4) representa o máximo dos valores de a[2], a[3], a[4] e a[5].
  • 197. a + k •  Em geral, sendo a um array e k um número inteiro, a expressão a + k representa o subarray de a que começa no elemento a[k]. •  Tal como com os arrays em geral, ao especificarmos um subarray, normalmente indicamos o seu tamanho, isto é, o número de elementos que queremos processar. •  Frequentemente, interessa-nos o resto do array; por outras palavras, se o array a tiver n elementos, o array a + k terá n – k elementos. 18/12/14 Programação Imperativa 197
  • 198. Exemplos: soma recursiva, máximo recursivo •  Usando subarrays, podemos processar arrays recursivamente. •  Observe, com atenção: 18/12/14 Programação Imperativa 198 double doubles_sum_r(double *a, int n) { return n == 0 ? 0 : a[0] + doubles_sum_r(a+1, n-1); } double doubles_max_r(double *a, int n) { return n == 0 ? -INFINITY : max(a[0], doubles_max_r(a+1, n-1)); } Em C, normalmente não se programa assim, mas a técnica é interessante e válida, em geral.
  • 199. Aplicação: problema da via do infante •  Dispomos de um ficheiro com o número de carros que passaram no pórtico de Loulé, minuto a minuto, num certo dia. •  Queremos saber qual foi o quarto de hora com mais trânsito. •  Solução: ler o ficheiro para um array e somar os sucessivos subarrays de tamanho 15, guardando os resultados noutro array. •  Depois, obter o argumento do máximo neste array e identificar o quarto de hora respetivo. 18/12/14 Programação Imperativa 199 ... 16 5 4 10 7 3 18 16 7 15 7 18 20 9 3 4 16 3 0 20 9 15 ...
  • 200. Somar de 15 em 15 •  Queremos somar os números de carros que passaram em cada minuto, para cada quarto de hora: 18/12/14 Programação Imperativa 200 int ints_sums_by_15(const int *a, int n, int *b) { int result = 0; for (int i = 0; i < n; i += 15) b[result++] = ints_sum(a+i, min(15, n - i)); return result; } Repare na utilização da função min, para o caso geral de n não ser múltiplo de 15. A variável de controlo cresce de 15 em 15.
  • 201. Somar grupos de comprimento fixo •  Com um pouco mais de esforço, programamos uma função mais geral, que soma grupos de comprimento dado: 18/12/14 Programação Imperativa 201 int ints_sums_by_tuple (const int *a, int n, int x, int *b) { int result = 0; for (int i = 0; i < n; i += x) b[result++] = ints_sum(a+i, min(x, n - i)); return result; }
  • 202. Função de teste •  Observe: 18/12/14 Programação Imperativa 202 void test_infant(void) { int a[1440]; int n = ints_get(a); int b[1440]; int m = ints_sums_by_tuple(a, n, 15, b); ints_println_basic(b, m); int z = ints_argmax(b, m); printf("%d %dn", z, b[z]); printf("%d %dn", z / 4, z % 4); } $ ../../sources/a.out < infant_data.txt 34 35 31 18 12 15 16 17 12 16 15 13 15 16 14 15 11 12 14 15 17 16 25 29 37 23 42 65 43 94 76 110 106 104 102 144 167 152 164 190 240 230 176 157 177 148 139 155 201 180 146 160 174 168 170 220 146 144 145 135 145 133 187 150 126 122 147 144 152 192 201 182 195 153 238 242 166 205 168 182 147 113 132 121 186 128 138 145 143 103 106 60 65 49 48 24 75 242 18 3 $ Para estes dados, o quarto de hora com mais trânsito foi o que começou às 18:45.
  • 203. Problema dos grupos •  Dado um array de double, construir um outro array (de int) com os comprimentos dos grupos de elementos consecutivos iguais. 18/12/14 Programação Imperativa 203 void unit_test_doubles_groups(void) { double a1[16] = {4,9,4,4, 4,7,7,7, 7,7,8,6, 6,6,6,4}; int b1[16]; int z1[7] = {1,1,3,5,1,4,1}; int m1 = doubles_groups(a1, 16, b1); assert(ints_equal_arrays(b1, m1, z1, 7)); double a2[8] = {4,4,4,4, 4,4,4,4}; int b2[8]; int z2[1] = {8}; int m2 = doubles_groups(a2, 8, b2); assert(ints_equal_arrays(b2, m2, z2, 1)); } int doubles_groups(const double *a, int n, int *b);
  • 204. Contar enquanto... •  Vamos basear-nos numa função que conta os elementos à cabeça do array que têm um dado valor. •  A função conta “enquanto” os elementos forem iguais ao valor dado: 18/12/14 Programação Imperativa 204 void unit_test_doubles_count_while(void) { double a[16] = {4,4,4,3, 5,9,9,5, 5,5,5,5, 5,5,1,1}; assert(doubles_count_while(a, 16, 4) == 3); assert(doubles_count_while(a, 16, 7) == 0); assert(doubles_count_while(a+4, 12, 5) == 1); assert(doubles_count_while(a+4, 12, 2) == 0); assert(doubles_count_while(a+8, 8, 5) == 6); assert(doubles_count_while(a+8, 8, 3) == 0); assert(doubles_count_while(a+14, 2, 1) == 2); assert(doubles_count_while(a+14, 2, 3) == 0); } int doubles_count_while(const double *a, int n, double x);
  • 205. Função doubles_count_while •  Na verdade, é uma variante da função de busca: 18/12/14 Programação Imperativa 205 int doubles_count_while (const double *a, int n, double x) { int result = 0; while (result < n && a[result] == x) result++; return result; }
  • 206. Função doubles_groups •  Por cada grupo, acrescenta-se o comprimento do grupo ao array de saída e avança-se no array de entrada: 18/12/14 Programação Imperativa 206 int doubles_groups(const double *a, int n, int *b) { int result = 0; int i = 0; while (i < n) { int z = doubles_count_while(a+i, n-i, a[i]); b[result++] = z; i += z; } return result; } Em cada passo do ciclo, detetamos um novo grupo, à cabeça do subarray seguinte. Esse grupo é formado pelos elementos que são iguais ao primeiro do grupo. Aprenda muito bem esta função!
  • 207. Problema da remoção de duplicados •  Dado um array de double, construir um outro array (de double) com um exemplar de cada grupo de elementos consecutivos iguais. 18/12/14 Programação Imperativa 207 void unit_test_doubles_unique(void) { double a1[16] = {4,9,4,4, 4,7,7,7, 7,7,8,6, 6,6,6,4}; double b1[16]; double z1[7] = {4,9,4,7,8,6,4}; int m1 = doubles_unique(a1, 16, b1); assert(doubles_equal_arrays(b1, m1, z1, 7)); double a2[8] = {4,4,4,4, 4,4,4,4}; double b2[8]; double z2[1] = {4}; int m2 = doubles_unique(a2, 8, b2); assert(doubles_equal_arrays(b2, m2, z2, 1)); } int doubles_unique(const double *a, int n, double *b); Não confunda com a função “nub”. Essa guarda a primeira ocorrência no array. Esta guarda um exemplar de cada grupo de elementos consecutivos iguais.
  • 208. Função doubles_unique •  Por cada grupo, acrescenta-se um elemento do grupo ao array de saída e avança-se no array de entrada: 18/12/14 Programação Imperativa 208 int doubles_unique(const double *a, int n, double *b) { int result = 0; int i = 0; while (i < n) { int z = doubles_count_while(a+i, n-i, a[i]); b[result++] = a[i]; i += z; } return result; } É praticamente igual à outra, não é?
  • 209. Versões recursivas •  As versões recursivas das funções groups e unique também são muito interessantes: 18/12/14 Programação Imperativa 209 int doubles_groups_r(const double *a, int n, int *b) { int result = 0; if (n > 0) { int z = doubles_count_while(a, n, a[0]); b[0] = z; result = 1 + doubles_groups_r(a+z, n-z, b+1); } return result; } int doubles_unique_r(const double *a, int n, double *b) { int result = 0; if (n > 0) { int z = doubles_count_while(a, n, a[0]); b[0] = a[0]; result = 1 + doubles_unique_r(a+z, n-z, b+1); } return result; }
  • 210. a[0] ≡ *a •  Em C, não se escreve a[0]. Em vez disso escreve-se *a, que significa o mesmo e usa menos carateres... •  Por exemplo: 18/12/14 Programação Imperativa 210 int doubles_unique_r2(const double *a, int n, double *b) { int result = 0; if (n > 0) { int z = doubles_count_while(a, n, *a); *b = *a; result = 1 + doubles_unique_r2(a+z, n-z, b+1); } return result; } Vá-se habituando...
  • 212. Arrays ordenados •  Renque. •  Busca dicotómica. •  Método da bisseção. 18/12/14 Programação Imperativa 212
  • 213. Problema do renque •  O renque de um valor num array é o número de elementos do array cujo valor é menor que esse valor: 18/12/14 Programação Imperativa 213 int ints_rank_general(const int *a, int n, int x); void unit_test_rank_general(void) { int a[10] = {8,3,9,8,4, 4,2,7,5,7}; assert(ints_rank_general(a, 10, 5) == 4); assert(ints_rank_general(a, 10, 12) == 10); assert(ints_rank_general(a, 10, 1) == 0); assert(ints_rank_general(a, 10, 2) == 0); assert(ints_rank_general(a, 10, 3) == 1); assert(ints_rank_general(a, 10, 7) == 5); assert(ints_rank_general(a, 5, 7) == 2); assert(ints_rank_general(a, 5, 3) == 0); assert(ints_rank_general(a, 5, 9) == 4); assert(ints_rank_general(a, 1, 5) == 0); assert(ints_rank_general(a, 1, 9) == 1); assert(ints_rank_general(a, 0, 5) == 0); } Chamamos renque geral porque se pode aplicar a um array qualquer. Para arrays ordenados, usaremos funções especializadas.
  • 214. int ints_rank_general_r(const int *a, int n, int x) { int result = 0; if (n > 0) result = (*a < x) + ints_rank_general_r(a+1, n-1, x); return result; } Renque geral •  Programa-se nas calmas: •  A versão recursiva também é interessante: 18/12/14 Programação Imperativa 214 int ints_rank_general(const int *a, int n, int x) { int result = 0; for (int i = 0; i < n; i++) if (a[i] < x) result++; return result; } É uma variante da função de contagem. Note bem: o valor aritmético das expressões lógicas é 0 ou 1.
  • 215. Arrays ordenados •  Um array está ordenado se o valor de cada elemento é menor ou igual ao valor do elemento seguinte. •  Essa propriedade é representada pela seguinte função lógica: 18/12/14 Programação Imperativa 215 void unit_test_ints_is_sorted(void) { int a[10] = {1,2,5,5,5, 6,8,8,9,9}; assert(ints_is_sorted(a, 10)); assert(ints_is_sorted(a, 1)); assert(ints_is_sorted(a, 0)); int b[10] = {3,5,5,2,4, 4,8,8,2,5}; assert(!ints_is_sorted(b, 10)); assert(ints_is_sorted(b, 3)); assert(!ints_is_sorted(b, 5)); assert(ints_is_sorted(b+3, 5)); assert(!ints_is_sorted(b+5, 5)); } int ints_is_sorted(int *a, int n);
  • 216. Função is_sorted •  Procura-se o primeiro par de elementos consecutivos fora de ordem: •  Também a versão recursiva: 18/12/14 Programação Imperativa 216 int ints_is_sorted(int *a, int n) { for (int i = 1; i < n; i++) if (a[i-1] > a[i]) return 0; return 1; } int ints_is_sorted_r(int *a, int n) { return n <= 1 || (*a <= a[1] && ints_is_sorted_r(a+1, n-1)); } Um array está ordenado se o seu tamanho for menor ou igual a 1 ou, sendo maior que 1, se o valor do primeiro elemento for menor ou igual ao do segundo e o resto do array (tirando o primeiro elemento) estiver ordenado.
  • 217. Renque em arrays ordenados •  O renque geral inspeciona todos os elementos do array e conta aqueles que são menores que o valor dado. •  Se o array estiver ordenado, conseguimos calcular o renque sem inspecionar os elementos todos. •  Aliás, conseguimos fazê-lo inspecionando relativamente poucos elementos. •  Vejamos como. •  Por hipótese, temos um array a, ordenado, com tamanho n, e é dado um valor x: queremos calcular o número de elementos de a cujo valor é menor que x. 18/12/14 Programação Imperativa 217
  • 218. Calculando o renque em arrays ordenados •  Tomemos um elemento qualquer de a, a[m]. •  Se x<=a[m], então x<=a[m+1], x<=a[m+2], etc., pois o array está ordenado; logo, todos os elementos de a cujo valor é menor que x estão à esquerda de a[m]. •  Inversamente, se x>a[m], então x>a[m-1], x>a[m-2], etc., porque o array está ordenado; logo, o valor de cada um dos elementos à esquerda de a[m] é menor que x e o valor de a[m] também. •  Sendo assim, no primeiro caso, basta contar os elementos de valor menor que x no subarray inicial com m elementos; no segundo, há pelo menos m+1 elementos com valor menor que x, a que se juntam os elementos menores que x no subarray a+(m+1). 18/12/14 Programação Imperativa 218