PROGRAMAÇÃO PASCAL Lição 1 – Introdução Introdução É uma linguagem de programação estruturada que recebeu este nome em homenagem ao matemático Blaise Pascal. Foi criada em 1970 pelo suíço Niklaus Wirth, tendo em mente encorajar o uso de código estruturado. O próprio Niklaus Wirth diz que Pascal foi criada simultaneamente para ensinar programação estruturada e para ser utilizada em sua fábrica de software. A linguagem é extremamente bem estruturada e muito adequada para ensino de linguagens de programação. É provavelmente uma das linguagens mais bem resolvidas entre as linguagens estruturadas, e certamente, um dos exemplos de como uma linguagem especificada por uma pessoa pode ser bem melhor do que uma linguagem especificada por um comitê. Pascal originou uma enorme gama de dialetos, podendo também ser considerada uma família de linguagens de programação. Grande parte de seu sucesso se deve à criação, na década de 80, da linguagem Turbo Pascal, inicialmente disponível para computadores baseados na arquitetura 8086 (com versões para 8080 no seu início). Pascal é normalmente uma das linguagens de escolha para ensinar programação, junto com Scheme, C, e Fortran. Pré-requisitos O objetivo deste curso não é ensinar a lógica de programação, assim, ele será melhor aproveitado pelos alunos que já tiverem conhecimentos básicos sobre ela. Será necessário instalar um compilador de pascal no seu computador para fazer os exercícios práticos. Escolha um dos compiladores da lista e instale antes de começar o curso. Para instalar o free pascal: #apt-get update #apt-get install fp-compiler
Como compilar e executar Sempre que você escrever um programa em Pascal ou quiser testar algum dos exemplos faça o seguinte:
• • • •
Abra um editor de texto qualquer (gedit, kate, kedit, etc) e escreva seu programa (ou copie e cole o exemplo); Salve o arquivo com a extensão .pas, por exemplo, teste.pas; Abra um terminal e digite fpc <nome_do_arquivo.pas>, isto irá compilar seu programa. Se houver algum erro nele que impessa será exibida uma mensagem na tela dizendo qual o erro; Digite então ./<nome_do arquivo>, agora você não deve digitar o .pas do nome do arquivo. O ./ executa o programa.
Lição 2 - Conceitos Iniciais Constantes e variáveis Constantes e variáveis são elementos básicos manipulados pelos programas. Ambos armazenam dados. As constantes armazenam dados que terão o mesmo valor durante toda a execução do programa. As variáveis armazenam dados que podem ter seus valores alterados durante a execução. Todas as constantes e variáveis de um programa em Pascal precisam ser declaradas no início do programa. Assim o computador poderá reservar o espaço de memória que será necessário para a execução do programa. Cada constante ou variável tem um nome que a identifica, ou um identificador, e um tipo. Os identificadores servem para que se tenha acesso aos dados, podendo vê-los e modificá-los quando for necessário. Existem algumas regras para os identificadores, são elas:
•
O primeiro caractere de um identificador deve ser obrigatoriamente uma letra;
• • •
Os caracteres seguintes podem ser letras ou dígitos; Não é permitido o uso de caracteres em branco ou quaisquer outros que não sejam letras ou dígitos; A linguagem Pascal não diferencia as letras maiúsculas das minúsculas, assim, Variavel3 e variavel3 são considerados o mesmo identificador.
Os tipos de dados do Pascal serão explicados a seguir: Para os dados numéricos o Pascal tem diferentes tipos tanto para inteiros quanto para reais. A diferença entre os tipos de cada um desses grupos é o intervalo de valores que cada um suporta. Por exemplo, os tipos Shortint e Integer armazenam inteiros. A diferença entre eles é que um dado do tipo Shortint pode assumir valores entre -128 e 127 e os dados do tipo Integer podem assumir valores de -32768 a 32767. A lista seguinte mostra todos os tipos de dados numéricos da linguagem Pascal e o intervalo de valores suportado por cada tipo. Tipos de dados inteiros:
• • • • •
Shortint -128 a 127; Integer -32768 a 327667; Longint -2147483648 a 2147483647; Byte 0 a 255; Word 0 a 65535.
Tipos de dados reais
• • • • •
Real 2,9 * 10-39 a 1,7 * 10 38 Single 1,5 * 10 -45 a 3,4 * 1038 Double 5,0 * 10-324 a 1,7 * 10 308 Extended 3,4 * 10-4932 a 1,1 * 104932 Comp -9,2 * 10 18 a 9,2 * 10 18
É importante escolher o tipo certo de dado para cada situação para que não haja desperdício de memória. Por exemplo, uma variável dia que representa os dias do mês poderá ter apenas valores inteiros de 0 a 31. Então, não faz sentido escolher para esta variável o tipo de dados real, ou longint. O tipo Byte é capaz de armazenar todos os valores possíveis de dia e é o que menos ocupa memória (quanto menor o intervalo de valores suportado pelo tipo menor, será a quantidade de memória alocada), então é a melhor escolha a ser feita. O tipo Shortint também poderia ser escolhido, pois ocupa a mesma quantidade de memória do tipo Byte e é capaz de armazenar valores inteiros de 0 a 31. Além dos tipos dados numéricos, o Pascal tem tipos de dados lógicos e de caracteres, mostrados abaixo: Tipos de dados caracteres:
• •
String; Char.
Tipos de dados lógicos:
• •
True; False.
As variáveis do tipo char armazenam um caractere (isto é, uma letra, um dígito ou um símbolo especial). As do tipo string armazenam uma cadeia de caracteres, podendo formar palavras. Uma string pode ter no máximo 255 caracteres. Os dados do tipo lógico só podem assumir os valores verdadeiro (true) ou falso (false). Uma variável, ou constante, lógica deve ser declarada como boolean. Declaração de Variáveis Você já sabe que as variáveis tem de ser declaradas, então, vejamos como se faz isto. Var nome: string [30];
idade: shortint; salário: comp; Antes das declarações utilizamos a palavra reservada var, que indica que as próximas linhas terão declarações de variáveis. Depois a sintaxe é a seguinte: <nome da variável> : ; Observe que ao final de cada linha é necessário usar um ponto-e-vírgula. Se houver mais de uma variável do mesmo é possível escrevê-las na mesma linha. Neste caso o nome das variáveis deve ser separado por vírgulas, como no exemplo abaixo: nota1, nota2, nota3 : real; também é permitido declarar variáveis de tipos diferentes em uma linha. nome: string [30]; idade: shortint; porém, como você deve ter percebido, isto dificulta a legibilidade do programa. Em alguns casos a declaração de muitas variáveis do mesmo tipo em uma linha também causa este problema. Declaração de Constantes Para declarar uma constante em Pascal basta escrever seu identificador e seu valor, precedidos da palavra const. Como foi feito abaixo: const nome = 'Ana Maria de Souza'; idade = 35; O valor atribuído a uma constante na declaração pode ser um real, inteiro, uma string ou um valor lógico. A sintaxe de uma declaração de constante é sempre igual a do exemplo acima: = ; As operações aritméticas básicas do Pascal são: soma, subtração, multiplicação, divisão, divisão inteira e resto. As operações de soma e subtração têm menor prioridade que as demais. Entre operações com a mesma prioridade será executada primeiro a que estiver mais à esquerda na expressão. A representação dos operadores em Pascal é mostrada a seguir: +
soma
_
subtração
*
multiplicação
/
divisão
div
divisão de inteiros
mod
resto da divisão de inteiros
Os operadores +, -, * e / podem ser usados tanto para reais quanto para inteiros. Os operadores div e mod só podem ser usados para números inteiros. Em expressões com os operadores +, - e * se um dos operandos for real o resultado será origatoriamente real. O operador / só produz resultados reais, independentemente do tipo dos operandos. Veja alguns exemplos: Considere i1 e i2 variáveis inteiras, e r1 e r2 variáveis reais,
• • • • • •
i1 + i2 tem resultado inteiro; i1 + r1 tem resultado real; i1 / i2 tem resultado real; i1 * i2 tem resultado inteiro; i1 mod i2 tem resultado inteiro; i1 mod r1 é uma operação inválida, já que os operandos de mod têm de ser necessariamente inteiros.
O Pascal tem também funções para realizar operações diferentes das que já foram citadas, como seno e exponenciação. A tabela seguir mostra estas funções. Função
Resultado
Tipo do resultado
ln(ea)
logarítmo natural de ea
real
exp(ea)
eelevado a ea
real
abs(ea)
módulo de ea
integer ou real
trunc(ea)
valor truncado de ea
integer
round(ea)
valor arredondado de ea
integer
sqr(ea)
ea elevado ao quadrado
integer ou real
sqrt (ea)
raíz quadrada de ea
real
sin(ea)
seno de ea
real
cos(ea)
cosseno de ea
real
arctan(ea)
arco tangente de ea
real
Obs: 1. ea é uma expressão aritmética. 2. truncar um número significa considerar apenas sua parte inteira. Por exemplo, truncando 4,87 teríamos 4 como resultado. Pergunta: De que tipo deverão ser as variáveis a, b, c, d e e para que o seguinte trecho de programa esteja correto? a := (b+c)/(d mod e) a: b: c: d: e:
real; integer; real; integer; integer;
Uma expressão lógica é uma expressão à qual podem ser atribuídos os valores verdadeiro ou falso. Chamamos de relação uma expressão que compara dois valores do mesmo tipo. Freqüentemente utiliza-se uma combinação de relações com expressões lógicas nos programas em Pascal. Os operadores relacionais estão listados na tabela seguinte. >
maior
>=
maior ou igual
<
menor
<=
menor ou igual
=
igual
<>
diferente
Podemos utilizar os operadores relacionais tanto para comparar dados numéricos quanto caracteres. A comparação entre strings é feita caractere a caractere de acordo com a ordem alfabética, assim, 'ana' < 'bruno'. Cada comparação retorna um valor lógica. Por exemplo, se a = 2 e b = 2.5, a comparação a > b retornará o valor false. Operador lógico, assim como um operador aritmético, é uma classe de operação sobre variáveis ou elementos pré-definidos. AND, OR e NOT são os principais operadores lógicos, base para a construção de sistemas digitais e da Lógica proposicional. Os operadores AND e OR são operadores binários, ou seja, necessitam de dois elementos, enquanto o NOT é para um único elemento. Uso:
• • •
x1 AND x2; x1 OR x2; NOT x1.
Descrição:
•
AND: operador lógico onde a resposta da operação é verdade (1) somente se ambas as variáveis forem verdade. x1 x2 x1 AND x2 0 0 0 0 1 0 1 0 0 1 1 1
•
OR: operador lógico onde a resposta da operação é verdade (1) se pelo menos uma das variáveis de entrada for verdade. x1 x2 x1 OR x2 0 0 0 0 1 1 1 0 1 1 1 1
•
NOT: operador lógico que representa a negação (inverso) da variável atual. Se ela for verdade, torna-se falsa, e viceversa. x1 NOT x1 0 1
OBS: nas tabelas acima o valor 0 equivale a false e o valor 1 equivale a true. Pergunta: Se a é uma variável do tipo boolean, qual o valor de a depois de ser executada a seguinte linha de código: a := (b or c) and d; onde as variáveis b, c e d têm respectivamente os valores false, true e false; FALSE A função de um comando de atribuição é modificar o valor de uma variável. A sintaxe de uma atribuição é a seguinte: <nome da variável> := <novo valor>; OBS: o novo valor pode ser uma expressão, por exemplo: salarioliquido := salariobruto - (dependentes * mensalidade); onde mensalidade é o valor pago para cada um dos dependentes para que ele tenha direito ao plano de saúde do funcionário. Antes de usar um comando de atribuição lembre-se de que a variável e o novo valor devem ser de tipos compatíveis. Por exemplo, se idade é uma variável inteira não poderíamos escrever idade := 'maria'; Mesmo entre um valor inteiro e uma variável inteira é preciso tomar algum cuidado para que não haja erros. Neste caso, o importante é verificar que o tipo de dado da variável é capaz de armazenar o novo valor. Veja um exemplo: numerodealunos := 400; se numerodealunos for do tipo integer não haverá nenhum problema com esta atribuição, porém, se ele for do tipo byte não será possível atribuir o novo valor a ele, pois variáveis deste tipo só podem assumir valores entre 0 e 255. O mesmo acontece com os reais. Antes de atribuir um valor a uma variável tenha certeza de que ela será capaz de armazenar este valor. O comando de atribuição também pode ser usado para variáveis lógicas (boolean). Veja um exemplo: aprovado := false; onde aprovado é uma variável booleana que recebe valor verdadeiro se o aluno obteve média maior ou igual a 6, e falso caso contrário. Pergunta: Qual das seguintes atribuições foi feita da maneira correta em Pascal? idade := 15; onde idade é uma variável do tipo integer. Lição 3 - Começando a Programar Agora que você já sabe um pouco sobre a linguagem Pascal começaremos a programar! Todo programa em Pascal tem a seguinte estrutura: Program <nome do programa>;
var <declarações de variáveis> begin end. Se o programa tiver constantes basta declará-las logo depois das variáveis, como foi mostrado na primeira lição. As palavras begin e end delimitam o bloco principal do programa, onde são escritos os comandos. program Hello; var mensagem : string; begin mensagem := 'Hello World'; write (mensagem); end. Copie e cole o programa acima no editor de texto. salve-o com o nome de hello.pas, agora abra o terminal, entre na pasta que você salvou o hello.pas e digite os seguintes comandos: $ fpc hello.pas $ ./hello O primeiro comando compila o programa, isto é, traduz o seu programa em Pascal para um código objeto, que posteriormente resultará num programa executável. O segundo comando executa o executável gerado após a compilação. Se tudo der certo depois destes comandos você verá na tela a frase Hello World. A maior parte do programa você já deve ter entendido. Primeiro declaramos uma variável do tipo string chamada mensagem. Depois, dentro do bloco principal do programa, atribuímos a mensagem à string Hello World. A última parte é a que você não tinha aprendido ainda, write (mensagem); faz com que o conteúdo da variável mensagem seja impresso na tela. Na próxima página você aprenderá mais alguns comandos de entrada e saída. Na página anterior foi usado um comando que ainda não tinha sido apresentado neste curso, o comando write. Você muito provavelmente percebeu que a função deste comando é escrever uma mensagem na tela. Existem outros comandos que têm a função de receber dados do usuário e mostrar informações a ele. Eles são chamados comandos de entrada e saída, e serão apresentados a seguir. Entrada
•
read
O comando read é usado para ler dados que o usuário digita. A sintaxe desse comando é a seguinte: read (lista de identificadores); quando a lista de identificadores tiver mais de um identificador, separe-os com vírgulas. Quando o programa é executado os valores devem ser escritos separados por espaços.
•
readln
O readln também tem como função ler dados da entrada padrão. Sua sintaxe é a seguinte: readln (lista de identificadores); A diferença entre estes dois comandos é que se em uma linha de entrada forem digitados mais valores do que os que o programa precisa ler no momento, o readln ignora os próximos valores. O read, ao contrário, utilizará estes valores como entradas na próxima vez que o programa tiver um comando de entrada. Veja um exemplo: program funcoesdeleitura; var i1, i2, i3: integer; r1, r2, r3: real; begin read (i1,r1); read (i2,r2); read (i3,r3); end. Suponha que o programa foi executado e o usuário digitou os seguintes valores: 1 3.2 3 3.8 2 2.4 <-'
5 4.2 8 2.5 9 4.2 <-' 6 4.3 7 7.3 4 5.5 <-' em que o símbolo <-' representa a tecla enter. Neste caso as variáveis i1, i2, i3 e r1, r2, r3 receberiam os seguintes valores: i1 = 1 r1 = 3.2 i2 = 3 r2 = 3.8 i3 = 2 r3 = 2.4 Se o mesmo programa tivesse sido escrito substituindo os read por readln os valores das variáveis seriam os seguintes: i1 = 1 r1 = 3.2 i2 = 5 r2 = 4.2 i3 = 6 r3 = 4.3 Isto porque depois de fazer r1 = 3.2 os demais valores da primeira linha seriam ignorados. O segundo read utilizaria os valores da segunda linha, fazendo i2 = 5 e r2 = 4.2. Saída Assim como para a entrada existem dois comandos para saída, write e writeln. A sintaxe desses comandos é idêntica à dos mostrados anteriormente. write (lista de identificadores); writeln (lista de identificadores); Como ja foi dito, a funcão deles é imprimir algo na tela. No lugar da lista de identificadores podemos colocar valores constantes, strings, expressões etc. Veja alguns exemplos: writeln ('Bom Dia'); writeln ('idade: ',idade); writeln ('media = ', lições*0.7 + prova*0.3); Se as variáveis tivessem os valores idade = 18; lições = 9; prova = 10. Os comandos acima teriam as seguintes saídas: Bom Dia; idade: 18; media = 9.3. A diferença entre os comandos write e writeln é que o segundo depois de imprimir a mensagem muda o cursor para a próxima linha. Assim, se houver vários writeln no programa, a saída de cada um deles será escrita em uma linha. Por outro lado, se houver vários write, todas as saídas serão escritas na mesma linha. Exercício 1 Escreva um programa que leia os dados de uma pessoa, nome, idade, número de identidade, telefone e endereço, e os imprima na tela. Lição 4 - Condicionais e Estruturas de Repetição Os programas em Pascal são executados sequencialmente, isto é, os comandos são executados um a um na ordem em que foram escritos. Porém, existem algumas estruturas que podem mudar esta ordem. Por exemplo, usando uma estrutura de repetição pode-se fazer com que o próximo comando executado não seja o da próxima linha, mas o que está cinco linhas acima do último comando executado. A seguir serão apresentadas as estruturas de decisão e repetição. Estruturas de Decisão Se você já conhece a lógica de programação, ou alguma outra linguagem, provavelmente se lembrará de que é muito comum ter de associar determinados comandos em um programa a condições. Por exemplo, se (média >= 6.0) então aprovado := true senão aprovado := false
Neste caso a condição para que uma pessoa seja aprovada é que sua média final seja maior ou igual a 6. Se isto não acontecer ela será reprovada (aprovado = false). A sintaxe das estruturas de repetição em Pascal é a seguinte: If condição; then comandos; {else comandos}. O que essa estrutura faz é primeiro verificar se a condição é verdadeira ou falsa. Se for verdadeira os comandos que vêm depois do then serão executados. Se for falsa os comandos depois do else serão executados. O else não é necessário. Você pode usar uma condição apenas para decidir se um comando será ou não executado, sem precisar dizer o que será feito se a condição não for verdadeira. Quando há mais de um comando a ser executado no then ou no else precisamos sinalizar quais são eles, caso contrário seria impossível distinguir estes comandos dos que vem depois da estrutura de repetição e devem ser executados independentemente da condição. Veja o exemplo abaixo: If (hora >= 16.0); then write ('O horário de funcionamento já foi encerrado'); write ('Obrigado por visitar nossa página'). Neste caso a linha write ('Obrigado por visitar nossa página'); deverá ser executada apenas se a condição hora >= 16.0 ou em todos os casos? Seria impossível saber isso. Por isso usamos blocos de comandos que delimitam quais comandos estão dentro de cada parte da estrutura. As palavras utilizadas para isso são begin e end. Veja outro exemplo: If (hora >= 16.0) then begin write ('O horário de funcionamento já foi encerrado'); write ('Por favor retorne amanha'); end else begin write ('por favor digite seu número de identificação'); read (numero); write ('por favor digite a senha de acesso'); read (senha); end; write ('Obrigado por visitar nossa página'); Neste caso fica claro que quando a condição for verdadeira será executado o bloco: begin write ('O horário de funcionamento já foi encerrado'); write ('Por favor retorne amanha'); end
e se a condição for falsa será executado o seguinte: begin write ('por favor digite seu número de identificação'); read (numero); write ('por favor digite a senha de acesso'); read (senha); end; e o comando write ('Obrigado por visitar nossa página'); será executado independentemente da condição. Outra forma de mudar a ordem seqüencial de execução de um programa é através das estruturas de repetição ou laços. Estas estruturas são usadas para que um mesmo bloco de comandos seja executado várias vezes. No Pascal existem três diferentes formas de fazer isto, for, while e repeat. For A primeira forma de laço estudada neste curso será o for. Nela é sempre usado um contador, que controla o número de vezes que o bloco será repetido. A sintaxe do for é a seguinte: for contador := valor-inicial to valor-final do begin bloco de comandos end; ou for contador := valor-inicial downto valor-final do begin bloco de comandos end; onde contador é a variável responsável por determinar quantas vezes o bloco ainda será executado. A cada vez que o laço é executado o valor de contador é incrementado (quando usamos o to) ou decrementado (quando usamos o downto). É importante observar que o contador deve ser sempre declarado como uma variável inteira. O laço será repetido até que o contador seja maior que o valor final, se for usado to, ou menor que ele, se for usado o downto. Observe os valores assumidos pelo contador na execução do seguinte programa: program UsandoFor; var contador: byte; begin for contador := 1 to 10 do writeln ('contador= ', contador); writeln (fim do laco); writeln ('contador= ', contador); end. A saída impressa na tela será a seguinte: contador= 1 contador= 2 contador= 3 contador= 4 contador= 5 contador= 6 contador= 7 contador= 8 contador= 9 contador= 10 fim do laço contador= 10
Ao final de cada execução do laço a variável contador é incrementada automaticamente. Isso acontece até que seu valor seja igual ao valor final (10). Então o próximo comando depois do for é executado. Observe que após o fim das repetições o contador terá o útilmo valor que recebeu no for, 10. Os valores inicial e final não precisam ser necessariamente constantes, podem ser variáveis inteiras, como no exemplo abaixo: program UsandoFor; var valorinicial, valorfinal : byte; contador : byte; begin writeln ('digite o valor inicial'); readln (valorinicial); writeln ('digite o valor final'); readln (valorfinal); for contador := valorinicial to valorfinal do writeln ('contador= ', contador); writeln ('fim do laco'); writeln ('contador= ', contador); end. Exercício: Agora que você já sabe como utilizar o laço for, escreva um programa que receba as notas finais de oito alunos da turma do curso de Pascal e calcule a média da turma. Como a média é igual a (soma das notas)/8, você precisará de uma variável que armazene a soma das notas. Depois de ter lido todas elas basta dividir o valor desta variável por 8. Depois modifique seu programa para que o usuário possa informar qual o número de alunos da turma. O for é um laço do tipo contado. Ou seja, há uma variável que conta quantas vezes ele já foi executado e o fim da execução acontece quando esta variável atinge um valor máximo ou mínimo. Os outros dois tipos de loops do Pascal (while e repeat) não são contados, são condicionais. Isto quer dizer que a execução não tem um número definido de vezes para acontecer. Seu fim depende de uma condição. Veja a sintaxe do while: while (condição) do begin comandos end; No exemplo abaixo o usuário pode definir até quando o loop será executado. A situação é a seguinte, deseja-se calcular a renda per capita de um condomínio, porém, o número de moradores é desconhecido. O programa é executado até que a renda de todos os moradores já tenha sido digitada. Para que o cálculo seja feito corretamente, se um morador não tem renda alguma será digitado para ele o valor 0. A execução do loop terminará quando for digitado o valor -1 para a renda. A condição é uma expressão lógica, que pode assumir os valores verdadeiro ou falso. Antes de entrar no laço esta condição é testada. Se ele for verdadeira os comandos de dentro do while serão executados. Se for falsa o loop termina. Program RendaPerCapita; var renda : integer; rendatotal : integer; nmoradores : integer; percapita : real; begin renda := 0; rendatotal := 0; nmoradores := 0; while (renda <> -1) do begin writeln ('digite a renda do morador'); readln (renda); if (renda >= 0)
then begin rendatotal := rendatotal + renda; nmoradores := nmoradores +1; end; end; percapita := rendatotal/nmoradores; writeln ('renda per capita do condiminio: ',percapita:6:2); end.
Como você já deve ter percebido as variáveis renda, rendatotal e nmoradores foram inicializadas com 0 no início do programa. Isto é importante porque diferentes compiladores podem atribuir diferentes valores a variáveis não inicializadas. No free pascal elas teriam o valor zero mesmo que você não atribuisse esse valor a elas, mas em outro compilador talvez tivessem um valor diferente. O zero é o melhor valor inicial porque antes de entrar no laço ainda não foi contado nenhum morador e nenhuma renda. Além disso renda = 0 permite que o laço comece. Neste exemplo há uma estrutura que não tinha sido mostrada ainda neste curso, um bloco de comandos dentro de outro (no if). Exercício Pesquise a função do :6:2 da última linha do último exemplo. O repeat é o segundo tipo de loop condicional do Pascal. A diferença entre ele e o while é que no while a condição é testada antes de entrar no laço, enquanto que no repeat ela é testada no final de cada repetição. A sintaxe deste laço é a seguinte: repeat comandos until condição; no repeat não é preciso utilizar begin e end para delimitar os comandos que fazem parte do laço, pois o until já faz isso. Como a condição de parada só é testada no final do laço todos os comandos serão executados pelo menos uma vez. Depois de terem sido executados a condição é testada. Se ele for verdadeira o programa não entrará de novo no laço, se for falsa repetirá todos os comandos e testará novamente a condição até que ela seja verdadeira. Sempre que você for utilizar loops tenha certeza de que há um momento em que o programa sairá deles. Caso contrário você terá um loop infinito. Lição 5 - Estruturas de Dados e Manipulação de Arquivos Até agora vimos apenas variáveis que armazenam um único dado, as variáveis simples. Existe um outro tipo de variáveis, as compostas. As variáveis compostas têm diversos dados armazenados. Elas correspondem a diferentes posições de memória identificadas por um único nome. A distinção entre essas posições é feita por índices. Variáveis compostas podem ser unidimensionais (vetores) ou multidimensionais (registros). Em Pascal ambas são chamadas de arrays. Tais variáveis podem ser ainda classificadas como homogêneas (quando todos os dados são de um mesmo tipo) ou heterogêneas (quando as variáveis são de tipos diferentes). Vetores A declaração de um vetor é feita da seguinte maneira: : array [..] of ; em que é o tipo dos componentes da variável e início e fim são os limites inferior e superior dos índices. Por exemplo, se queremos um vetor que armazene os nomes dos 30 alunos de uma turma podemos fazer o seguinte: nomes : array [1..30] of string; Matrizes As variáveis compostas homogêneas multidimensionais são chamadas matrizes. Se um vetor pode ser representado por uma seqüência linear de elementos, uma matriz pode ser representada por uma tabela (se for bidimensional). Não há um limite para o número de dimensões de uma matriz. Esse tipo de estrutura é comumente utilizado quando é necessária mais de uma informação para distinguir um componente de outro. A sintaxe é bastante parecida com a dos vetores. A única diferença é que para as matrizes é necessário informar os limites superior e inferior de cada dimensão. : array [..,..,.......,..] of ; em que ,,,, etc são os limites inferior e superior dos índices de cada dimensão da matriz. O exemplo seguinte mostra um trecho de programa que armazena em um vetor o nome e o endereço 10 pessoas e depois os imprime na tela.
program Agenda; var i: byte; lista : array [1..10,1..2] of string; nome,endereco : string; begin for i:= 1 to 10 do begin writeln ('nome?'); readln (nome); lista[i,1] := nome; writeln ('endereco?'); readln (endereco); lista[i,2] := endereco; end; for i:= 1 to 10 do begin writeln (i,'-> nome:',lista[i,1],' endereco:',lista[i,2]); end; end. Registros Um registro é um conjunto de dados de diferentes tipos relacionados. O registro é identificado por um nome. Cada campo de um registro também tem um nome que o identifica dentro do registro. Por exemplo, para construir um registro que contém os dados de um filme declararíamos o registro filme com os campos nome, gênero, autor, duração e censura. Esta declaração seria feita da seguinte forma: var filme : record nome : string; genero : string; autor: string; duração : real; censura : integer; end; Podemos também declarar os registros como tipos e depois declarar variáveis destes tipos. A vantagem deste tipo de declaração é que podemos declarar várias variáveis com a mesma estrutura sem precisar repetir a declaração acima. Basta dizer uma vez quais serão os campos. Depois podemos declarar quantas variáveis quisermos com os mesmos campos. No exemplo abaixo declaramos um tipo endereço como sendo um registro com os campos cidade, bairro, rua, número. Depois declaramos uma variável dadospessoais que é um registro onde um dos campos é do tipo endereço. type endereco = record cidade : string; bairro : string; rua : string; numero : integer; end; var dadospessoais : record nome: string; idade : byte; residencia : endereço; telefone : string; end; Para acessar um dado de um registro usamos a sintaxe: identificador.nomedocampo Se quisermos, por exemplo, saber o autor do filme armazenado em filme podemos escrever: writeln (filme.autor); E para imprimir o nome da rua da pessoa cujos dados estão na variável dadospessoais fazemos o seguinte: writeln (dadospessoais.endereco.rua); Arrays de Registros
Podemos construir arrays de registros da mesma forma que fizemos com variáveis simples. Para fazer um vetor de filmes por exemplo teríamos a seguinte declaração: Type filme = record nome : string; genero : string; autor: string; duração : real; censura : integer; end; Var filmes : array [1..15] of filme; Arquivos Arquivos são estruturas de dados armazenadas fora da memória principal e que podem ser acessadas durante a execução do programa. Isto inclui discos rígidos, fitas magnéticas, disquetes, DVDs ou qualquer outro dispositivo que possa ser acessado pelo computador onde ocorre a execução. Os arquivos podem ser de dois tipos, binários ou de texto. Ambos serão explicados a seguir. Declaração de Arquivos A declaração de um arquivo é feita da seguinte forma: type <nome do tipo> = file of var <nome da variável> : file of em que: nome do tipo é o nome o identificador do novo tipo; tipo é o tipo dos dados do arquivo, pode ser text, string, real, record, etc. Exemplo 1 type endereco = record cidade : string; bairro : string; rua : string; numero : integer; end; arqend : file of endereco; var arq1: arqend; Exemplo 2 var arquivotexto : text; OBS : text é o tipo dos arquivos de texto. Operações com arquivos As principais operações que podem ser realizadas sobre arquivos são, abertura, fechamento, leitura e escrita. Antes de abrir um arquivo é preciso associá-lo à variável declarada no começo. Para isso usamos o comando assign, que tem a seguinte sintaxe: assign (nome da variável, nome do arquivo); poderíamos por exemplo escrever:
assign (arquivo, 'nomes.txt'); Depois de associar o nome do arquivo à variável precisamos abri-lo. Existem diferentes tipos de abertura do arquivo, dependendo do que será feito com ele: Reset (nome da variável) - Abre o arquivo que foi associado à variável para leitura. Este comando só funciona se o arquivo já existir, caso contrário haverá erro de execução; Rewrite (nome da variável) – Abre o arquivo que foi associado à variável para escrita. Se o arquivo ainda não existir ele será criado pelo programa. Porém, se ele já existir todo seu conteúdo será apagado. Só use este comando quando você quiser que os dados sejam apagados ou quando o arquivo ainda não existir; Append (nome da variável) – Abre o arquivo que foi associado à variável para escrita. Este comando só funciona se o arquivo já existir, caso contrário haverá erro de execução. Se já houver algum conteúdo no arquivo ele será preservado e os novos dados serão escritos no final do arquivo. As operações básicas sobre um arquivo são leitura e escrita. Os comandos de leitura e escrita em arquivos são os mesmos que utilizamos para a entrada e saída padrão, read, readln, write e writeln. A única diferença é que antes do nome da variável a ser lida ou escrita, ou da string a ser escrita devemos escrever o identificador da variável correspondente ao arquivo. Veja alguns exemplos: Read (arqnomes, nome); Readln (arqnomes, nome); Write (arq1, identidade); Writeln (arq2, 'Hello World'); OBS: os comandos readln e writeln só podem ser usados para arquivos de texto, para arquivos binários utiliza-se apenas read e write. Se você abrir um arquivo para leitura não poderá escrever nele, e se abri-lo para escrita não poderá ler dados dele. Depois de executar todas as operações necessárias em um arquivo você deve fechá-lo. Isto garante a corretude dos dados após o término do programa. Nunca deixe um arquivo aberto após a execução. O comando utilizado para fechar arquivos é o close e sua sintaxe é a seguinte: close (nome da variável); Arquivos binários Existem algumas operações que só podem ser realizadas em arquivos binários. Algumas delas são descritas abaixo: seek (nome do arquivo, n) - Acessa o n-ésimo registro do arquivo. n deve ser menor que o número total de registros do arquivo. É importante lembrar que para o primeiro registro do arquivo n=0. Assim, se queremos acessar o quinto registro do arquivo arq fazemos: seek (arq, 4); Se você não souber o número total de registros do arquivo use o comando filesize. A sua sintaxe é: filesize (nome do arquivo); A linha de código abaixo imprime o número de registros do arquivo arq: writeln('numero de registros: ',filesize(arq)); Localizando o fim do arquivo Em muitas situações você precisará verificar se o arquivo já terminou ou se ainda há registros nele. Para isto o Pascal tem a função eof (end of file) que retorna true se o arquivo já terminou e false caso contrário. Veja um exemplo de utilização desta função: repeat readln (arquivo, nome); until eof (arquivo); este bloco lê do arquivo uma string nome até que chegue o fim do arquivo.
Modularizar um programa significa dividi-lo em pequenas partes simples. Cada parte deve realizar uma tarefa e ao final o conjunto das partes deve ser capaz de fazer tudo que a especificação do programa exige. Estas partes, ou subprogramas, em Pascal são classificadas como funções ou arquivos. Entre as vantagens de se modularizar um programa estão:
• • • •
Maior legibilidade; Facilidade de manutenção; Possibilidade de divisão do trabalho para um grupo de pessoas; Possibilidade de reaproveitamento de código (um trecho do programa que se repete várias vezes poderá ser escrito apenas uma vez).
Nas próximas páginas você aprenderá a utilizar procedimentos e funções. Procedures Procedimentos são subprogramas. Isto é, programas menores que ficam dentro de um programa principal. Sendo assim, sua estrutura é bastante semelhante à dos programas vistos até agora. procedure <nome do procedimento> (lista de parâmetros); var { declaração de variáveis } begin { comandos} end; A primeira diferença que pode ser vista entre um programa principal e um procedure é a lista de parâmetros. Parâmetros são variáveis declaradas fora do procedure que serão utilizadas dentro dele. É por meio deles que é feita a comunicação do procedimento com a parte do programa externa a ele. Não é obrigatório que um subprograma receba parâmetros, mas muitas vezes eles ajudam a possibilidade de reutilização do software. O programa seguinte contém um procedimento que troca os valores das variáveis v1 e v. procedure troca (var v1, v2: integer); var aux: integer; begin aux:=v1; v1:=v2; v2:=v1; end; Na seção var são declaradas as variáveis locais do procedure. Também é possível declarar nesta parte constantes e tipos, como foi feito em seções anteriores do curso. Os procedures são escritos antes do programa principal. A estrutura de um programa com um procedure é a seguinte: Program <nome do programa>; const {declarações de contantes tipos e variáveis globais que forem necessárias} type var Procedure . begin . . end; Begin . . . End. Note que os comandos do programa principal só começam depois de todos os procedures. Para utilizar um procedimento dentro do programa principal basta escrever no local onde ele deve ser chamado seu nome e os parâmetros de entrada, caso existam, por exemplo: Program CalculaResto; Procedure resto(a,b,r:integer); begin r:= a mod b; writeln ('o resto e; ', r); end;
var a1,a2,r1: integer; begin writeln (r1); writeln ('a1?'); read (a1); writeln ('a2?'); read (a2); resto (a1,a2,r1); writeln ('r1: ', r1); end. Neste programa a linha resto (a1,a2,r1); chama o procedure resto com os parâmetros a1, a2, r1. Você deve ter reparado que mesmo após a execução do procedimento a variável r1 continuou tendo o valor com o qual foi inicializada, 0. Isto aconteceu porque os procedimentos não alteram o valor dos parâmetros a não ser que essa intenção seja explicitada. No último exemplo após a execução de resto todos os parâmetros mantiveram os valores que tinha antes da chamada de resto. Se o valor dos parâmetros deve ser modificado após a execução do procedure usamos a palavra var antes da lista de parâmetros, como no próximo exemplo: Program CalculaResto; Procedure resto(var a,b,r:integer); begin r:= a mod b; writeln ('o resto e; ', r); end; var a1,a2,r1: integer; begin r1 := 0; writeln ('r1: ', r1); writeln (r1); writeln ('a1?'); read (a1); writeln ('a2?'); read (a2); resto (a1,a2,r1); writeln ('r1: ', r1); end. Exercício Faça uma pesquisa sobre a técnica de ordenação booble sort. Então crie um programa que use o procedure troca e ordene em ordem crescente os dados de um vetor de inteiros de tamanho 5. Functions Funções também são subprogramas. A diferença entre functions e procedures é que as primeiras retornam um valor ao final de sua execução, os procedures não. A sintaxe de uma function é a seguinte: Function <nome da function> (lista de parâmetros) : tipo retornado; var { declaração de variáveis } begin { comandos} end; onde tipo retornado é o tipo do valor que a função retorna. A função do exemplo seguinte recebe como parâmetros a altura e a largura de um retângulo e retorna o comprimento de sua diagonal. function Diagonal (altura, largura : real): real; begin Diagonal := sqrt (sqr(altura)+sqr(largura)); end; Agora podemos escrever um programa maior que pergunta ao usuário a altura, largura e profundidade de um paralelepípedo e calcula sua diagonal. Para fazer isso será preciso primeiro calcular a diagonal da base do paralelepípedo (formada por largura e profundidade). Depois calculamos a diagonal do retângulo formado pela diagonal da base e a altura do paralelepípedo, que é igual a diagonal do paralelepípedo.
Program DiagParalelepipedo; function Diagonal (altura, largura : real): real; begin Diagonal := sqrt (sqr(altura)+sqr(largura)); end; var altura, largura, profundidade : real; diag: real; aux: real; begin writeln ('altura?'); readln (altura); writeln ('largura?'); readln (largura); writeln ('profundidade'); readln (profundidade); aux := Diagonal (largura, profundidade); diag := Diagonal (aux, altura); writeln ('A diagonal do paralelepipedo e: ',diag); end.