9.3 Alguns exemplos
9.4.1 Calculando ra´ızes de equa¸c˜ oes do segundo grau
Para refor¸carmos os conceitos em estudo, consideremos aqui o problema de se ler os coeficientes a, b e c que definem uma equa¸c˜ao do segundo grau ax2 +bx+c = 0 e imprimir as ra´ızes calculadas pela f´ormula de B´askara. O programa deve imprimir mensagens corretas no caso de n˜ao haverem ra´ızes reais bem como n˜ao deve aceitar entradas cujo valor para o coeficienteasejam nulas. O programa da figura 9.11 cont´em o c´odigo que resolve este problema.
A figura 9.12 ilustra o programa principal modificado para se dar a ideia de que as fun¸c˜oes se comportam como express˜oes aritm´eticas, ao contr´ario dos procedimentos, que se comportam como comandos.
Na verdade, o programa principal poderia ser apenas o c´odigo da figura 9.13, sem preju´ıjo do funcinamento, mas com bem menos legilidade e ainda por cima sem tratamento do delta negativo. Apresentamos esta vers˜ao apenas para ilustrar o uso das fun¸c˜oes dentro de fun¸c˜oes, mas observamos que o c´alculo do delta ´e feito duas vezes.
program raizes eq 2o grau ;
var a , b, c , delta , x1 , x2 : real; procedure l e r (var a , b, c : real) ; begin
{$i−}
repeat
read (a , b, c) ;
until ( ioresult = 0) and (a<> 0) ; {$i+}
end;
function calcula delta (a , b, c : real) : real; begin
calcula delta:= b∗b−4∗a∗c ; end;
function menor raiz (a , b, delta : real) : real; begin
menor raiz:= (−b− sqrt ( delta ) )/(2∗a) ; end;
function maior raiz (a , b, delta : real) : real; begin
maior raiz:= (−b + sqrt ( delta ) )/(2∗a) ; end;
begin (∗ programa principal ∗)
l e r (a , b, c) ; (∗ garante−se que a nao eh nulo ∗) delta:= calcula delta (a , b, c) ;
i f delta>= 0 then begin
x1:= menor raiz (a , b, delta ) ; writeln (x1) ;
x2:= maior raiz (a , b, delta ) ; writeln (x2) ;
end else
writeln (’raizes complexas’) ; end.
Figura 9.11: Calculando ra´ızes de equa¸c˜ao do segundo grau.
9.4. C ´ALCULO DO MDC PELA DEFINIC¸ ˜AO 123
begin (∗ programa principal ∗)
l e r (a , b, c) ; (∗ garante−se que a nao eh nulo ∗) delta:= calcula delta (a , b, c) ;
i f delta>= 0 then begin
writeln (menor raiz (a , b, delta ) , maior raiz (a , b, delta ) ) ; end
else
writeln (’raizes complexas’) ; end.
Figura 9.12: Calculando ra´ızes de equa¸c˜ao do segundo grau.
begin (∗ programa principal ∗)
l e r (a , b, c) ; (∗ garante−se que a nao eh nulo ∗) writeln (menor raiz (a , b, calcula delta (a , b, c) ) , maior raiz (a , b, calcula delta (a , b, c) ) ) ; end.
Figura 9.13: Calculando ra´ızes de equa¸c˜ao do segundo grau.
Terminamos aqui a primeira parte do curso, no qual as no¸c˜oes fundamentais sobre algoritmos est˜ao estabelecidas. Nos pr´oximos estudaremos as principais estruturas de dados b´asicas para um curso introdut´orio de algoritmos.
9.5 Exerc´ıcios
1. Fazer uma fun¸c˜ao em Pascal que receba como parˆametro dois n´umeros intei-ros n˜ao nulos e retorne TRUE se um for o contr´ario do outro e FALSE em caso contr´ario. Isto ´e, se os parˆametros forem 123 (cento e vinte e trˆes) e 321 (trezentos e vinte e um), deve-se retornar TRUE. Usar apenas opera¸c˜oes sobre inteiros.
2. Fazer uma fun¸c˜ao em Pascal que receba como parˆametro um n´umero inteiro representando um n´umero bin´ario e retorne seu valor equivalente em decimal.
Por exemplo, se a entrada for 10001, a sa´ıda deve ser 17.
3. Fazer uma fun¸c˜ao em Pascal que receba como parˆametro um n´umero inteiro e retorne TRUE se ele for primo e FALSE em caso contr´ario. Use esta fun¸c˜ao para imprimir todos os n´umeros primos entre 0 e 1000.
4. Implemente fun¸c˜oes para seno e cosseno conforme definidos em cap´ıtulos an-teriores e use-as em uma terceira fun¸c˜ao que calcule a tangente. O programa principal deve imprimir os valores de tg(x) para um certo valor fornecido pelo usu´ario.
5. Implemente um procedimento para gerar mais de um milh˜ao de n´umeros inteiros.
Os n´umeros gerados dever˜ao ser impressos em um arquivo texto.
6. Fa¸ca uma fun¸c˜ao em Pascal que some dois n´umeros representando horas. A entrada deve ser feita da seguinte maneira:
12 52 7 13
A sa´ıda deve ser assim:
12:52 + 7:13 = 20:05
7. Fa¸ca uma fun¸c˜ao que receba como parˆametros seis vari´aveis DIA1, MES1 e ANO1, DIA2, MES2 e ANO2, todas do tipo integer. Considerando que cada trinca de dia, mˆes e ano representa uma data, a fun¸c˜ao deve retornar true se a primeira data for anterior `a segunda e false caso contr´ario.
Cap´ıtulo 10
Estruturas de dados
At´e aqui apresentamos as t´ecnicas b´asicas para constru¸c˜ao de algoritmos, incluindo as no¸c˜oes de fun¸c˜oes e procedimentos. Podemos dizer que ´e poss´ıvel, com este conte´udo, programar uma vasta cole¸c˜ao de algoritmos, inclusive alguns com alta complexidade.
Contudo, o estudo geral da disciplina de “Algoritmos e Estrurutas de Dados”
envolve algoritmos que trabalham dados organizados em mem´oria de maneira mais sofisticada do que as simples vari´aveis b´asicas que foram estudadas at´e o momento.
E algo mais ou menos parecido como manter um guarda-roupas organizado no lugar´ de um monte de coisas atiradas no meio do quarto de qualquer jeito.
A organiza¸c˜ao de dados em mem´oria permite a constru¸c˜ao de algoritmos sofistica-dos e eficientes. Neste textp estudaremos trˆes estruturas de dados elementares. S˜ao elas:
• vetores (ou array unidimencional);
• matrizes (ou array multidimencional);
• registros.
Nas se¸c˜oes seguintes explicaremos cada uma delas, sempre motivados por proble-mas que exigem seu uso ou que facilitam a implementa¸c˜ao. Tamb´em veremos nos pr´oximos cap´ıtulos algumas estruturas que misturam estas trˆes.
10.1 Vetores
Para motivar, vamos considerar o problema seguinte: ler uma certa quantidade de valores inteiros e os imprimir na ordem inversa da leitura. Isto ´e, se os dados de entrada forem: 2, 5, 3, 4, 9, queremos imprimir na sa´ıda: 9, 4, 3, 5, 2.
Este tipo de problema ´e imposs´ıvel de ser resolvido com o uso de apenas uma vari´avel pois, quando se lˆe o segundo n´umero, j´a se perdeu o primeiro da mem´oria.
Exigiria o uso de tantas vari´aveis quantos fossem os dados de entrada, mas notem que isto deve ser conhecido em tempo de compila¸c˜ao! O que faz com que simplesmente n˜ao seja poss´ıvel resolver este problema para uma quantidade indefinida de valores.
125
De fato, quando se aloca, por exemplo, um n´umero inteiro em uma vari´avel de nomea, o que ocorre ´e que o computador reserva uma posi¸c˜ao de mem´oria em algum endere¸co da RAM (conforme sabemos pelo modelo Von Neumann). Um inteiro exige (dependendo da implementa¸c˜ao) 2 bytes.
Mas, digamos que ´e preciso alocar espa¸co para 100 n´umeros inteiros. Sabendo que cada um deles ocupa 2 bytes, precisar´ıamos encontrar uma maneira de reservar 100×2 = 200 bytes e fazer com que este espa¸co de mem´oria pudesse ser acessado tamb´em por um ´unico endere¸co, ou em outras palavras, por uma ´unica vari´avel.
Os vetores s˜ao estruturas de dados que permitem o acesso a uma grande quantidade de dados em mem´oria usando-se apenas um nome de vari´avel. Esta vari´avel especial
´e declarada de tal maneira que o programador passa a ter acesso `a muitas posi¸c˜oes de mem´oria, de maneira controlada.
Sem ainda entrar em detalhes da linguagemPascal, vamos tentar entender o pro-cesso.
Como funciona isto em mem´oria?
Seguindo no exemplo de se alocar 200 espa¸cos em mem´oria para n´umeros inteiros, suponhamos que o seguinte comando faz esta aloca¸c˜ao:
var V: array[ 1 . . 2 0 0 ] of integer;
EmPascal isto resulta na aloca¸c˜ao de 200 vezes 2 bytes. Pela vari´avel V temos o controle deste espa¸co.
O problema ´ecomo se faz para se escrever um valor qualquer neste espa¸co. Outro problema ´eonde se escreve, j´a que temos 200 possibilidades de escolha. O simples uso da vari´avel, como est´avamos acostumados, n˜ao serve. ´E preciso uma outra informa¸c˜ao adicional para se dizerem qual das 200 posi¸c˜oes se quer escrever.
Na verdade, a vari´avel V aponta para o in´ıcio do segmento reservado, da mesma maneira que se fazia para vari´aveis b´asicas j´a estudadas. Para se escrever em algum lugar deste segmento, ´e preciso informar, al´em do nome da vari´avel, uma segunda informa¸c˜ao: a posi¸c˜ao (ou o deslocamento) dentro do espa¸co reservado.
Ora, sabemos que foram reservadas 200 posi¸c˜oes, cada uma delas com espa¸co para conter um n´umero inteiro. Se quisermos escrever na quinta posi¸c˜ao, basta informar ao computador que o in´ıcio do segmento ´e dado pela vari´avel V e que, antes de se escrever, ´e preciso realizar um deslocamento de 5 posi¸c˜oes, cada uma delas para um inteiro. Isto d´a um deslocamento de 10 bytes. Ap´os esta informa¸c˜ao, o valor pode ser escrito. Se o desejo ´e escrever na d´ecima quarta posi¸c˜ao, o deslocamento deve ser de 14×2 bytes, isto ´e, 28 bytes.
Para se recuperar a informa¸c˜ao, por exemplo para se imprimir, ou para se fazer algum c´alculo com estes dados, basta usar o mesmo processo: os dados s˜ao acessados pelo nome da vari´avel e pelo deslocamento.
Este processo foi apresentado em muito baixo n´ıvel. Como de costume, precisamos de uma outra forma de representa¸c˜ao de mais alto n´ıvel. Isto ´e, cada linguagem de
10.1. VETORES 127 programa¸c˜ao que implementa a no¸c˜ao de vetores tem que encontrar uma maneira para se mascarar para o programador este processo que ´e baseado em deslocamentos (ou somas de endere¸cos).
Na pr´oxima se¸c˜ao veremos como a linguagem Pascal lida com isto.
Vetores em Pascal
Para se declarar um vetor de 200 posi¸c˜oes inteiras, a linguagem Pascal usa a seguinte sintaxe (lembre-se que em outras linguagens a sintaxe pode ser diferente):
var v : array [ 1 . . 2 0 0 ] of integer;
A constru¸c˜ao “1..200” indica que existem 200 posi¸c˜oes controladas pela vari´avel v. O “of integer” indica que cada posi¸c˜ao ´e para se guardar um n´umero inteiro, isto
´
e, 2 bytes (dependendo da implementa¸c˜ao).
A rigor, a linguagemPascal permite que se reserve 200 posi¸c˜oes de v´arias maneiras.
Basta que o intervalo “a..b” contenha 200 posi¸c˜oes. Apresentamos 6 variantes dentre as milhares poss´ıveis:
var v : array [ 0 . . 1 9 9 ] of integer; var v : array [201..400] of integer; var v : array [−199..0] of integer; var v : array [−300..−99] of integer; var v : array [−99..100] of integer; const min=11, max=210;
var v : array [min . .max] of integer;
Em todas estas variantes, o intervalo define 200 posi¸c˜oes. Em termos gerais, existe uma restri¸c˜ao forte. O intervalo deve ser definido em termos de n´umeros de algum tipo ordinal (em Pascal), isto ´e, integer, logint, . . . , at´e mesmo char. Tamb´em em Pascal, o limite inferior deve ser menor ou igual ao limite superior. Na sequˆencia desta se¸c˜ao, vamos considerar a vers˜ao que usa o intervalo de 1 a 200.
Agora, para guardar um valor qualquer, digamos 12345, na posi¸c˜ao 98 do vetor v, em Pascal, se usa um dos dois comandos seguintes:
v[98]:= 12345;
read(v[ 9 8 ] ) ; (∗ e se digita 12345 no teclado ∗)
Em termos gerais, vejamos os seguintes exemplos, para fixar o conceito:
read (v [ 1 ] ) ; (∗ l e do teclado e armazena na primeira posicao de v ∗) i := 10;
v [ i +3]:= i ∗ i ; (∗ armazena o quadrado de i (100) na posicao 13 de v ∗) write ( i , v [ i ] ) ; (∗ imprime o par (10, 100) na tel a ∗)
write (v [ v [ 1 3 ] ] ) ; (∗ imprime o valor de v[100] na t ela ∗) v[201]:= 5; (∗ gera erro , pois a posicao 201 nao existe em v ∗)
v[47]:= sqrt (4) ; (∗ gera erro , pois sqrt retorna um real , mas v eh de inteiros ∗) var x : real;
v [ x]:= 10; (∗ gera erro , pois x eh do tipo real , deveria ser ordinal ∗)
Note que a constru¸c˜ao (v[v[13]]) s´o ´e poss´ıvel pois o vetor v ´e do tipo integer. Se fosse um vetor de reais isto n˜ao seria poss´ıvel (em Pascal).