Introdução à Programação
Série
Didáctica
CIÊNCIAS APLICADAS
226
Manuel José Cabral dos Santos Reis
António Jorge Gonçalves de Gouveia
Francisco de Sousa Pereira
Universidade de Trás-os-Montes e Alto Douro
Vila Real
Índice
1 – Motivação........................................................................................................................... 11
2 – Breve História dos computadores ...................................................................................... 15
3 – Sistemas de numeração ...................................................................................................... 17
3.1 – Conversão directa entra as bases 2, 8, 16. ................................................................... 21
3.1.1 – Conversão de binário para octal/hexadecimal...................................................... 22
3.1.2 – Conversão de octal/hexadecimal para binário...................................................... 22
4 – Sistemas informáticos ........................................................................................................ 25
4.1 – Classificação segundo gerações de computadores ...................................................... 26
4.2 – Classificação quanto ao tamanho ou à “capacidade” do computador ......................... 27
4.3 – Classificação quanto ao número de utilizadores simultâneos ..................................... 29
4.4 – Principais componentes de um sistema informático ................................................... 31
4.4.1 – A placa principal ou motherboard........................................................................ 31
4.4.2 – O processador....................................................................................................... 32
4.4.3 – Memórias.............................................................................................................. 34
4.4.4 – O barramento ou bus de um sistema informático................................................. 37
4.4.5 – Ligações de periféricos ou dispositivos de I/O .................................................... 38
4.5 – Sistema Operativo ....................................................................................................... 46
5 – Algoritmos.......................................................................................................................... 49
5.1 – Noção de algoritmo ..................................................................................................... 51
5.2 – Dados e tipos de dados ................................................................................................ 52
5.3 – Operadores e operações básicas .................................................................................. 54
5.4 – Variáveis e expressões ................................................................................................ 55
5.5 – Entrada e saída de dados ............................................................................................. 57
5.6 – Funções embutidas ou pré-definidas ........................................................................... 57
6 – Estruturas de decisão.......................................................................................................... 59
6.1 – Selecção de acções alternativas – SE-ENTÃO-SENÃO............................................. 59
6.2 – SE’s encaixados........................................................................................................... 63
6.3 – Estrutura de selecção múltipla..................................................................................... 66
7 – Ciclos.................................................................................................................................. 71
7.1 – Estrutura REPETIR-ENQUANTO ............................................................................. 71
7.2 – Estrutura REPETIR-ATÉ............................................................................................ 74
7.3 – Entrada de dados através de ciclos.............................................................................. 78
7.4 – Ciclos contados ........................................................................................................... 79
8 – Subalgoritmos – funções e procedimentos......................................................................... 85
8.1 – Parâmetros e argumentos ............................................................................................ 85
8.2 – Tipos de passagem de parâmetros............................................................................... 86
8.3 – Funções........................................................................................................................ 87
8.4 – Procedimentos ............................................................................................................. 89
8.5 – Variáveis globais e variáveis locais ............................................................................ 90
8.6 – Subprogramas encaixados ........................................................................................... 92
9 – Vectores e matrizes ............................................................................................................ 95
9.1 – Arrays unidimensionais – Vectores............................................................................. 95
9.1.1 – Tratamento de strings ou cadeias de caracteres como arrays unidimensionais.. 101
9.2 – Arrays bidimensionais ou matrizes ........................................................................... 102
9.3 – Arrays multidimensionais ......................................................................................... 105
iii
10 – Pesquisa e ordenação com vectores ............................................................................... 107
10.1 – Ordenação ou classificação ..................................................................................... 107
10.1.1 – Ordenação por selecção.................................................................................... 107
10.1.2 – Ordenação por borbulhamento ......................................................................... 109
10.2 – Pesquisa................................................................................................................... 111
10.2.1 – Pesquisa linear.................................................................................................. 111
10.2.2 – Pesquisa binária................................................................................................ 112
11 – Registos e arrays de registos.......................................................................................... 115
11.1 – Arrays de registos.................................................................................................... 117
12 – Bibliografia..................................................................................................................... 121
iv
Índice de Figuras
Figura 1 – Exemplos de válvulas. ............................................................................................ 27
Figura 2 – Exemplos de transístores. ....................................................................................... 27
Figura 3 – Microprocessador Intel 4004. ................................................................................. 27
Figura 4 – Exemplos de processadores. ................................................................................... 27
Figura 5 – Computadores de pequeno, médio e grande porte. ................................................. 28
Figura 6 – Computador pessoal tipo secretária (desktop) “Indigo 223”. ................................. 28
Figura 7 – PC portátil. .............................................................................................................. 29
Figura 8 – Servidor de terminais. ............................................................................................. 30
Figura 9 – Rede de computadores. ........................................................................................... 30
Figura 10 – Computador típico. ............................................................................................... 31
Figura 11 – Motherboard típica. .............................................................................................. 31
Figura 12 – Fotografias de alguns processadores..................................................................... 32
Figura 13 – Constituição típica de um CPU e de um sistema informático. ............................. 33
Figura 14 – Memória primária tipo ROM................................................................................ 34
Figura 15 – Memória primária tipo RAM................................................................................ 35
Figura 16 – Memória secundária, auxiliar ou de armazenamento tipo disco rígido. ............... 35
Figura 17 – Organização da memória. ..................................................................................... 36
Figura 18 – Núcleo de memória de ferrite. .............................................................................. 36
Figura 19 – Superfície de um disco rígido de 20 Mbytes. ....................................................... 37
Figura 20 – Superfície de um CD com música dos “Bon Jovi”. .............................................. 37
Figura 21 – Sistema de computador típico. Note-se a presença de três tipos de bus............... 38
Figura 22 – Esquema simplificado do modo de funcionamento de um CRT. ......................... 39
Figura 23 – Exemplo de CRT policromático (cores). .............................................................. 40
Figura 24 – Exemplo de impressora “laser”............................................................................. 41
Figura 25 – Exemplo de impressora “jacto de tinta”. .............................................................. 42
Figura 26 – Exemplo de traçador gráfico tipo tambor. ............................................................ 42
Figura 27 – Exemplo de traçador gráfico tipo mesa. ............................................................... 43
Figura 28 – Exemplo de teclado, rato, painel táctil e lápis óptico. .......................................... 44
Figura 29 – Exemplo de mesa digitalizadora, joystick e trackball........................................... 44
Figura 30 – Exemplo de digitalizador 3D, spaceball e luva instrumentada............................. 45
Figura 31 – Exemplo de placa de som. .................................................................................... 45
Figura 32 – Exemplo de placa de rede. .................................................................................... 46
Figura 33 – Componentes principais de um sistema operativo................................................ 47
Figura 34 – Fases principais na resolução de um problema..................................................... 49
Figura 35 – Fases de desenvolvimento/teste de um algoritmo/programa. ............................... 50
Figura 36 – Tipos de dados. ..................................................................................................... 53
Figura 37 – Diagrama de fluxo da construção SE-ENTÃO-SENÃO. ..................................... 60
Figura 38 – Diagrama de fluxo da construção SE-ENTÃO..................................................... 60
Figura 39 – Diagrama de fluxo para determinação do maior de dois valores.......................... 61
Figura 40 – Diagrama de fluxo da estrutura REPETIR-ENQUANTO. ................................... 72
Figura 41 – Diagrama de fluxo da estrutura REPETIR-ATÉ. ................................................. 75
Figura 42 – Diagrama de fluxo para entrada de dados (lista não vazia). ................................. 79
Figura 43 – Diagrama de fluxo para entrada de dados (lista vazia). ........................................ 79
Figura 44 – Ilustração do processo de ‘passagem de controlo de fluxo ’. ............................... 85
Figura 45 – Interpretação gráfica de vector. ............................................................................ 96
v
Figura 46 – Interpretação geométrica de array bidimensional............................................... 102
vi
Índice de Tabelas
Tabela 1 – Representação dos números de 0 a 16 nas bases 2, 5, 8, 10 e 16. .......................... 18
Tabela 2 – Constituição básica de um sistema informático. .................................................... 25
Tabela 3 – Gerações de computadores..................................................................................... 26
Tabela 4 – Evolução de alguns parâmetros dos microprocessadores da família Intel. ............ 33
Tabela 5 – Tipos de memórias. ................................................................................................ 34
Tabela 6 – Exemplos de funções pré-definidas........................................................................ 58
vii
Índice de Ilustrações
Ilustração 1 – Classificação de computadores por tamanho/capacidade.................................. 28
Ilustração 2 – Classificação dos computadores quanto ao número de utilizadores. ................ 29
ix
1 – Motivação
Estes apontamentos surgem no âmbito da disciplina de Introdução à Programação (IP) do
1º ano, 2º semestre, do curso de Engenharia Electrotécnica da Universidade de Trás-osMontes e Alto Douro. Surgem na sequência da experiência adquirida pelos autores ao longo
destes últimos anos em que leccionaram os conteúdos aqui apresentados. Têm por principal
objectivo servir de base de estudo à referida disciplina. Estão divididos em duas partes, que se
relacionam directamente com a forma como a disciplina se encontra estruturada: nesta parte
(primeira) apresentam-se os conceitos, noções e técnicas para a resolução e implementação de
um dado algoritmo (componente teórica da disciplina); e numa segunda parte apresenta-se
uma lista de exercícios e as respectivas propostas de solução comentadas (componente prática
da disciplina). Esta segunda componente será publicada oportunamente.
Para além desta disciplina, que é a primeira disciplina do curso de Engenharia
Electrotécnica em que os alunos estabelecem contacto com as técnicas de programação de
computadores, o plano actual de estudos do curso contempla as seguintes disciplinas, cujos
assuntos aí tratados directa ou indirectamente se relacionam com os desta:
Algoritmos; 2º ano – 1º semestre;
Estruturas de Dados; 2º ano – 2º semestre;
Arquitectura de Computadores; 3º ano – 1º semestre;
Microprocessadores e Microcomputadores; 3º ano – 2º semestre;
Sistemas de Operação; 3º ano – 2º semestre;
Comunicação de Dados; 4º ano – 1º semestre;
Técnicas Avançadas de Computação; 4º ano – 2º semestre;
Redes de Computadores; 4º ano – 2º semestre;
Processamento Digital do Sinal; 4º ano – 2º semestre;
Telecomunicações; 4º ano – 2º semestre;
Algoritmos e Arquitecturas para PDS; 5º ano – 1º semestre;
Controlo Digital; 5º ano – 1º semestre;
11
Processamento Digital de Imagem; 5º ano – 1º semestre;
Computação Gráfica; 5º ano – 2º semestre;
Robótica; 5º ano – 2º semestre;
Projecto; 5º ano – anual.
As primeiras questões que se podem colocar são as seguintes: porquê utilizar
computadores?; e porquê aprender a programá-los? Obviamente que a resposta a estas duas
questões não é de forma alguma breve ou sucinta. Antes de mais convém ter presente o
âmbito em que elas são formuladas. Convém ainda esclarecer que o principal objectivo destas
questões é alertar o aluno para a necessidade da existência de tais competências (utilizar e
programar o computar). Assim passamos a listar algumas das áreas onde a informática foi
introduzida:
•
Na administração pública;
•
Em actividades empresariais;
•
No sistema educativo;
•
No sistema de saúde;
•
Na comunicação Social;
•
Etcetera.
Da mesma forma, os principais tipos de aplicações informáticas (programas) são:
•
Processamento de texto;
•
Edição electrónica;
•
Folhas de cálculo (e outros tipos de cálculo);
•
Bases de dados;
•
Desenho, Computer Aided Design – CAD;
•
Acesso à Internet e utilização dos seus serviços (browsers, clientes de e-mail, etc.);
•
Modelação e simulação computacional;
•
Inteligência artificial;
•
Etcetera.Resumindo, com o advento do computador e dos sistemas informáticos em
particular, passamos a ter:
•
Um elevado poder de cálculo dos computadores (maior capacidade e velocidade de
processamento);
12
•
Uma melhoria na qualidade da documentação produzida (por exemplo em termos de
CAD, Processamento de texto, Gráficos);
•
Ferramentas específicas para a Engenharia Electrotécnica (o que implica maior
competência e produtividade);
•
Facilidade de comunicação (E-mail, Internet, videoconferência, etc.);
•
Acesso à informação (Multimédia, CD-ROM, DVD, www, etc.).
O programa da disciplina pretende cobrir um conjunto de noções, técnicas e conceitos
que estejam de acordo com o nome e o tipo da disciplina, ou seja, com a introdução à
programação. Por este motivo não abordaremos conceitos como os de reutilização de código,
optimização, entre outros.
Muitas das figuras e tabelas aqui apresentadas não são de forma alguma originais,
resultando na sua grande maioria da digitalização directa de imagens dos livros, cujas
referências se juntam no final, e do resultado de pesquisas efectuadas na Internet.
13
2 – Breve História dos computadores
Tal como se pode depreender do título desta secção, não é nosso objectivo indicar de
forma exaustiva qualquer História dos computadores. É apenas pretendido fazer um
enquadramento histórico e introduzir a noção de que os computadores sofreram e sofrem um
processo de evolução continuado. Indicaremos apenas algumas das datas e factos que
consideramos como marcos na História da evolução dos computadores.
As primeiras ferramentas de cálculo surgiram na Babilónia, cerca de 3000 AC, e são
conhecidos por Ábacos. Em 1622, em Inglaterra, William Oughtred desenvolve a régua de
cálculo. Mais tarde em França (1642), Blaise PASCAL constrói a primeira máquina de
cálculo numérico.
A primeira máquina à qual podemos dar o nome de computador foi desenhada por
Charles Babbage em 1833, designada por Máquina Analítica, e recebe instruções a partir de
cartões perfurados.
O primeiro computador electrónico foi concluído em 1946 na Universidade da
Pensilvânia. Com o nome de ENIAC (Electronic Numerical Integrator and Computer)
possuía 18000 válvulas electrónicas, media 3 x 30 metros e pesava cerca 80 toneladas.
Realizava 5000 adições e 360 multiplicações por segundo.
Em 1975 o MITS desenvolve o primeiro computador pessoal, o Altair. Baseado no
microprocessador 8080 da INTEL, incluía 256 bytes de memória.
A IBM desenvolve o IBM PC em 1981. Possuía 64 Kbyte de memória RAM, uma drive
de floppy disk e um monitor monocromático. O seu sistema operativo era o MS-DOS,
desenvolvido por uma pequena empresa chamada Microsoft.
A Apple anuncia o seu Macintosh em 1984.
Da mesma forma, os microprocessadores sofreram uma forte evolução ao longo das
últimas décadas. O primeiro microprocessador, o 4004, surge em 1971 e foi desenvolvido
pela INTEL. Possuía 2300 transístores, trabalhava a uma frequência de 108 KHz e processava
informação de 4 bits. Em 1997 a INTEL lançou o Pentium II que possuia 7,5 milhões de
transístores e podia funcionar a velocidades de até 450 MHz. Em 2003 a INTEL possui
processadores Pentium 4, que funcionam a frequências superiores a 3 GHz e possuem mais de
15
100 milhões de transístores. Este processador possui um bus de endereços de 36 bits, um bus
de dados de 64 bits, o tamanho dos registos é de 32 bits e dois níveis de memória cache.
O primeiro sistema operativo foi desenvolvido em 1954 por Gene Amdahl sendo
utilizado no IBM 704. Em 1970 Ken Thompson e Dennis Ritchie desenvolvem o UNIX. Em
1981 a Microsoft desenvolve o MS-DOS para o IBM PC e em 1990 apresenta o Windows 3.0.
O ENIAC era programado, modificando ligações eléctricas entre os diversos
componentes! A primeira linguagem de programação foi desenvolvida por John Backus em
1954 para a IBM, sendo denominada de FORTRAN. Em 1964, Tom Kurtz e John Kemeny,
desenvolvem o BASIC (Beginners All-purpose Symbolic Instruction Code). Em 1969,
Nicklaus Wirth, escreve o primeiro compilador de PASCAL.
16
3 – Sistemas de numeração
O processamento de toda a informação num sistema informático é feito na base digital ou
binária; esta base de representação numérica é constituída por dois símbolos apenas: 0 (zero)
e 1 (um).
Em termos eléctricos, geralmente o 0 é representado por um nível de tensão baixo e o 1
por um nível de tensão alto. Todos os dispositivos ou circuitos digitais se regem por esta
regra, desde o processador à memória, passando pelos discos, etcetera.
A unidade mínima de representação é designada por BIT (BInary digiT). Um bit possui
pois o valor 0 ou 1. A um grupo de 8 bits dá-se o nome de byte; 1024 (210) bytes designam um
Kilo byte (Kbyte); 1024 Kbytes designam um Mega byte (Mbyte); 1024 Mbytes designam um
Giga byte (Gbyte); 1024 Gbytes designam um Tera byte (Tbyte); etcetera. Actualmente
existem autores que atribuem à unidade K o valor de 1000 (K = 100) e à unidade Ki o valor
1024 (Ki = 1024).
O homem utiliza o sistema de numeração decimal, base 10, devido ao facto de possuir
dez dedos nas mãos. Isto obriga à conversão de dados Decimal/Binário (à entrada) e
Binário/Decimal (à saída), de forma a tornar viável a utilização de computadores.
Numa base b de um sistema de numeração, define-se o conjunto de b símbolos para
representar cada valor. Por exemplo,
•
A base 10 é composta 0, 1, 2, 3, 4, 5, 6, 7, 8, 9;
•
A base 2 é composta
0, 1;
•
A base 5 é composta
0, 1, 2, 3, 4;
•
A base 16 é composta 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, F;
•
A base b é composta
0, 1, ..., (b -1).
A representação de um número na própria base é sempre ‘10’, uma vez que uma base b só é
composta pelos símbolos de 0 a b–1. Por exemplo,
•
2 na base 2 = 10;
•
5 na base 5 = 10;
17
•
10 na base 10 = 10;
•
16 na base 16 = 10.
n
Utilizando um conjunto de n dígitos numa dada base b, podemos ter b valores diferentes que
n
vão desde 0 a b –1. Como exemplos temos,
•
base 10 com 3 dígitos à 0 ... 999 (103 à 1000 valores);
•
base 2 com 8 dígitos à 0 ... 255 (28 à 256 valores);
•
base 3 com 4 dígitos à 0 ... 80 (34 à 81 valores).
Base 2
Base 5
Base 8
Base 10
Base 16
00000
0
0
0
0
00001
1
1
1
1
00010
2
2
2
2
00011
3
3
3
3
00100
4
4
4
4
00101
10
5
5
5
00110
11
6
6
6
00111
12
7
7
7
01000
13
10
8
8
01001
14
11
9
9
01010
20
12
10
A
01011
21
13
11
B
01100
22
14
12
C
01101
23
15
13
D
01110
24
16
14
E
01111
30
17
15
F
10000
31
20
16
10
Tabela 1 – Representação dos números de 0 a 16 nas bases 2, 5, 8, 10 e 16.
A Tabela 1 mostra-nos as representações dos números de 0 a 16 representados nas bases 2, 5,
8, 10 e 16.
18
Na representação de um número numa dada base, os dígitos possuem, para além do seu
valor absoluto, um valor posicional que define o seu peso. Na sua forma mais genérica, um
número N é composto por uma parte inteira e uma parte decimal ou fraccionária:
N = dn-1 dn-2 … d1 d0 , d-1 d-2 … d-p
parte inteira
parte decimal
Uma sucessão de dígitos designa um número igual à soma dos valores posicionados dos
respectivos dígitos:
n-1
Nb = dn-1 x b
n-2
+ dn-2 x b
-p+1
1
0
-1
-2
+ … + d1 x b + d0 x b + d-1 x b + d-2 x b + …
-p
+ d-p x b .
+ d-p+1 x b
Veja-se o caso concreto do número 1998,35 representado na base 10:
3
2
1
0
-1
-2
N10 = 1998,35 = 1 x 10 + 9 x 10 + 9 x 10 + 8 x 10 + 3 x 10 + 5 x 10 .
Na conversão de uma base b para uma base b’ devemos ter em atenção que a aritmética a
usar deve ser a da base destino. Contudo, a base que intervém directamente nos cálculos é a
de origem. Neste processo, multiplica-se o valor absoluto do digito pelo peso que possui na
representação do número, isto é, pela base elevada à posição do digito
Nb = dn-1 dn-2 … d1 d0 , d-1 d-2 … d-p
Nb' = dn-1 x bn-1 + dn-2 x bn-2 + … + d1 x b1 + d0 x b0 + d-1 x b-1 + d-2 x b-2 + …
-p+1
-p
+ d-p x b .
+ d-p+1 x b
Vejamos um exemplo. Suponhamos que possuíamos o número 110,11 representado na base 2
e pretendíamos saber a sua representação equivalente na base 10:
N2 = 110,11 à N10 ?
2
1
0
-1
-2
N10 = 1 x 2 + 1 x 2 + 0 x 2 + 1 x 2 + 1 x 2 = 6,75.
Outro exemplo. Pretende-se saber a representação do número 132 escrito na base 4 na sua
equivalente da base 5:
N4 = 132 à N5 ?
2
1
0
N5 = 1 x 4 + 3 x 4 + 2 x 4 = 31 + 22 + 2 = 103 + 2 = 110.
19
Note-se que 42=1610 e 3 x 4 = 1210. Agora, recorrendo à Tabela 1 para ajuda na representação
dos valores intermédios e atenção que a aritmética utilizada é a da base 5, facilmente se chega
ao resultado apresentado.
A conversão de uma base b para a base 10 é mais apetecível do que qualquer outra, uma
vez que a aritmética a utilizar é a da base 10, isto é, aquela em que estamos habituados a fazer
cálculo desde crianças. Comparem-se os dois exemplos visto acima. Vejamos mais dois
exemplos:
N2 = 1010,011 à N10 ?
3
2
1
0
-1
-2
-3
N10 = 1 x 2 + 0 x 2 + 1 x 2 + 0 x 2 + 0 x 2 + 1 x 2 + 1 x 2 = 10,375.
N16 = ABC à N10 ?
2
1
0
N10 = 10 x 16 + 11 x 16 + 12 x 16 = 2784 (uma vez mais recorremos à Tabela 1
para saber qual o correspondente decimal dos valores A, B e C).
Na conversão da base 10 para uma base b, separa-se a conversão da parte inteira do
número da parte decimal.
Na conversão da parte inteira, divide-se o número e os sucessivos quocientes pela base b,
guardando o resto de cada divisão, até atingir o quociente zero. A representação do número na
nova base é dada pela sequência invertida dos restos. Por exemplo, suponhamos que
pretendemos converter o número 10,375 no seu equivalente da base 2. Para isso começamos
por converter a parte inteira, isto é, o valor 10:
10 2
0
5 2
1
2 2
0
1 2
1
0
A parte inteira possui pois a representação 1010.
Para converter a parte fraccionária, multiplica-se sucessivamente o seu valor, e as partes
fraccionárias resultantes, depois de retiradas as partes inteiras, pela nova base até que a parte
fraccionária seja nula ou se tenha obtido uma “precisão conveniente”. Os sucessivos dígitos
20
inteiros assim obtidos formam a representação da parte fraccionária na nova base.
Solidifiquemos o algoritmo com a continuação da conversão do exemplo anterior (10,375):
0,375 x 2 = 0,75
0,75 x 2
= 1,5
0,5 x 2
= 1,0
isto é, a parte decimal vale 0,011. O resultado final é pois 1010,011.
A conversão da parte fraccionária é muitas vezes infinita, isto é, nunca se atinge zero no
processo das multiplicações sucessivas. Torna-se então necessário estabelecer um critério de
paragem. Este critério está relacionado com a capacidade de numeração ou de aproximação.
Admitindo para um número fraccionário na base b, um erro igual ao peso da posição p menos
significativa do número nessa base, bp, deve verificar-se para a posição menos significativa da
representação da nova base um erro b'p’ ≤ bp. Isto obriga a termos p' ≥ p x ln(b)/ln(b') para que
se mantenha uma aproximação equivalente. Veja-se um exemplo concreto. Suponhamos que
pretendíamos converter o número 0,468 escrito na base 10 para o seu equivalente na base 2.
Para isso devemos calcular p' ≥ 3 x ln(10)/ln(2) = 9,97 o que implica p' = 10 (não existe 0,97
de um dígito). O número 3 na expressão anterior é devido ao facto do número na base 10 estar
escrito com 3 casas decimais, o 10 do primeiro logaritmo neperiano é devido ao facto de
0,468 estar escrito na base 10 e o 2 no segundo logaritmo por ser a base na qual pretendemos
representar este número. Deixando os cálculos ao cuidado do aluno, o resultado final será pois
igual a 0,0111011111 (com a mesma aproximação).
Como conclusão podemos escrever que podemos utilizar a base 10 como intermediária
para converter um número de uma base b para uma base b’ e assim utilizar unicamente a
aritmética da base 10.
3.1 – Conversão directa entra as bases 2, 8, 16.
Repare-se que 23=8 e 24 = 16. É de esperar que exista alguma relação entre estas bases
que nos permita facilitar a conversão directa. O mesmo se poderia esperar de outras bases (por
exemplo as bases 3, 9 e 27). As conversões entre as bases 2, 8, e 16 estão de facto
simplificadas.
21
3.1.1 – Conversão de binário para octal/hexadecimal.
Decompõe-se o número a partir da vírgula, nos dois sentidos, em grupos de três/quatro
dígitos, completando-se com zeros à esquerda e à direita quando necessário. Depois, substituise cada um dos grupos formados pelo respectivo símbolo octal/hexadecimal.
Veja-se o seguinte exemplo. Com o número 11010010,1111 representado na base 2
construímos os grupos 011 010 010 à esquerda da vírgula e os grupos 111 100 à direita da
vírgula que possuem as representações 3, 2, 2, 7 e 4, respectivamente, na base 8 (veja-se a
Tabela 1). O resultado final será pois 322,74. De uma forma visual
N2 = 11010010,1111 à N8 ?
011 010 010 , 111 100
3
2
2 , 7
4
N8 = 322,74.
Dividindo agora o número em grupos de quatro, obtém-se a sua representação na base 16:
N2 = 11010010,1111 à N16 ?
1101 0010 , 1111
D
2 , F
N16 = D2,F.
3.1.2 – Conversão de octal/hexadecimal para binário.
Este é o processo inverso ao descrito no ponto anterior. Assim, substitui-se cada
algarismo octal/hexadecimal pela respectiva representação binária em três/quatro dígitos.
Vejamos os exemplos. Primeiro convertamos o número 346,15 escrito na base 8 ou octal para
a base 2:
N8 = 346,15 à N2 ?
3
4
6 , 1
5
011 100 110 , 001 101
N2 = 011100110,001101 = 11100110,001101.
22
Agora convertamos o número 2BD,C3 escrito na base 16 para a base 2:
N16 = 2BD,C3 à N2 ?
2
B
D , C
3
0010 1011 1101 , 1100 0011
N2 = 001010111101,11000011 = 1010111101,11000011.
23
4 – Sistemas informáticos
Os sistemas informáticos são constituídos por dois tipos de componentes: hardware –
parte física (componentes electrónicos, mecânicos e electromecânicos); e software – parte
lógica (programas). Veja-se a Tabela 2.
No hardware, geralmente distingue-se entre:
•
O computador, que inclui a unidade de processamento central – CPU;
•
Os periféricos, onde se incluem o teclado, o rato, o monitor, a impressora,
etcetera.
No software distingue-se geralmente entre:
•
Software de sistema, ou seja, o próprio sistema operativo;
•
Software de aplicação, onde se incluem os programas de computador, e.g.,
processamento de texto, folhas de cálculo, gestão de bases de dados, desenho,
tratamento de imagens, etcetera.
Software
De aplicação
Processadores de texto; bases de dados; CAD; etc.
De sistema
Sistema operativo
CPU
Hardware
Dispositivos
de saída
Dispositivos
de entrada
Memória
Tabela 2 – Constituição básica de um sistema informático.
Os sistemas informáticos podem classificar-se segundo várias características, sendo as
principais:
•
As sucessivas gerações de computadores;
•
O tamanho ou a “capacidade” do computador;
•
O número de utilizadores com que o sistema pode trabalhar simultaneamente.
25
4.1 – Classificação segundo gerações de computadores
No que se refere à classificação segundo gerações, até à data consideram-se quatro.
Os computadores da primeira geração surgiram por volta de 1945 e funcionavam a
válvulas, com dimensões e consumos gigantescos e reduzida capacidade de cálculo, à luz da
tecnologia actual.
Os da segunda geração surgiram por volta de 1955 e funcionavam com transístores,
possuindo dimensões e consumos mais reduzidos, havendo um aumento da capacidade de
cálculo. É nesta altura que surgem os primeiros sistemas operativos. Tem também início a sua
comercialização em meios empresariais.
A terceira geração surgiu por volta de 1965. Os computadores desta geração funcionavam
com chips – pequenas pastilhas de circuitos integrados (CIs) – e as dimensões e consumos
voltaram a cair, aumentando o poder de cálculo. Esta geração de computadores foi
comercializada em empresas e universidades.
A quarta geração surge por volta de 1970 e os computadores funcionam com
microprocessadores – processadores totalmente incluídos num chip graças às técnicas de Very
Large Scale Integration (VLSI). As suas dimensões são cada vez mais pequenas e os preços
cada vez mais baixos, conduzindo ao aparecimento dos computadores pessoais (Personal
Computers – PCs).
Há quem classifique os computadores actuais como computadores da quinta geração
(multiprocessamento ou processamento em paralelo, capacidade para funcionarem com
sistemas avançados de inteligência artificial – comunicação verbal, entre outras capacidades).
Na Tabela 3 apresenta-se um resumo desta classificação. Nas Figura 1, Figura 2, Figura 3 e
Figura 4 apresentam-se alguns exemplos dos componentes principais de um computador.
Geração
1ª
2ª
3ª
4ª
Período (aproximado)
1945-1955
1955-1965
1965-1970
1970-????
Característica principal
Válvulas
Transístores
Chips ou CIs
Microprocessadores
Tabela 3 – Gerações de computadores.
26
Escala
Milisegundo
Microsegundo
Nanosegundo
Picosegundo
Figura 1 – Exemplos de válvulas.
Figura 2 – Exemplos de transístores.
Figura 3 – Microprocessador Intel 4004.
Figura 4 – Exemplos de processadores.
4.2 – Classificação quanto ao tamanho ou à “capacidade” do computador
Quanto à classificação segundo o tamanho/capacidade, os computadores podem dividirse em grande porte, médio porte e pequeno porte. Os supercomputadores e mainframes
27
enquadram-se no primeiro grupo, os minicomputadores e workstations no segundo, e os
microcomputadores e ultramicrocomputadores no terceiro. Na Ilustração 1 apresenta-se um
resumo desta classificação. Nas Figura 5, Figura 6 e Figura 7 apresentam-se exemplos de
computadores.
Computadores
grande
porte
Supercomputadores
Mainframes
médio
porte
Minicomputadores
Workstations
pequeno
porte
Microcomputadores
Ultramicrocomputadores
Ilustração 1 – Classificação de computadores por tamanho/capacidade.
Figura 5 – Computadores de pequeno, médio e grande porte.
Figura 6 – Computador pessoal tipo secretária (desktop) “Indigo 223”.
28
Figura 7 – PC portátil.
4.3 – Classificação quanto ao número de utilizadores simultâneos
No que se refere ao número de utilizadores, pode considerar-se sistemas monoposto e
sistemas multiposto. Os primeiros podem ainda dividir-se em monoposto–monotarefa e
monoposto–multitarefa. Os segundos podem dividir-se em sistemas multiposto e redes de
computadores. Na Ilustração 2 resume-se esta classificação. Nas Figura 8 e Figura 9
apresentam-se dois exemplos de sistemas deste tipo.
Sistemas
monoposto
Monoposto – monotarefa
Monoposto – multitarefa
Sistemas
multiposto
Sistemas multiposto
Redes de computadores
Computadores
Ilustração 2 – Classificação dos computadores quanto ao número de utilizadores.
29
Figura 8 – Servidor de terminais.
Estações
de trabalho
Impressora
Servidor
Scanner
Estações
de trabalho
Figura 9 – Rede de computadores.
30
4.4 – Principais componentes de um sistema informático
Quando olhamos para dentro de uma caixa de computador do tipo PC, podemos
identificar, como componentes fundamentais, a placa principal ou motherboard, as placas de
expansão, as drives, a fonte de alimentação e os cabos de ligação. Na Figura 10 pode ver-se o
aspecto genérico de um computador pessoal do tipo desktop.
Figura 10 – Computador típico.
4.4.1 – A placa principal ou motherboard
Na motherboard podemos identificar o CPU ou processador, a memória RAM, a
memória ROM, os circuitos ou chips de controlo, os slots de expansão e os vários
barramentos ou buses. Na Figura 11 pode ver-se uma motherboard típica.
Slots de
expansão
BUS
Chips de
controlo
RAM
Local do CPU
Figura 11 – Motherboard típica.
31
4.4.2 – O processador
O processador ou Unidade Central de Processamento (Central Processing Unit – CPU) é
o componente fundamental de um computador. É ele que faz todo o processamento de dados
que lhe chegam através da memória e dos dispositivos de entrada. Os dados resultantes das
operações de processamento são depois reenviados para a memória ou para os periféricos de
saída.
O CPU consiste basicamente num circuito integrado, constituído por quatro unidades
fundamentais: unidade de controlo; unidade de aquisição/descodificação; unidade aritmética e
lógica (ALU); e os registos. Na Figura 12 podem ver-se alguns processadores.
Figura 12 – Fotografias de alguns processadores.
Dito de uma forma muito resumida, a unidade de controlo envia aos outros componentes
sinais de activação e sincronização das operações. Fazendo uma analogia com uma orquestra,
esta unidade funciona como o maestro. Por seu lado, a unidade de aquisição/descodificação é
a responsável pela execução das instruções e consequentemente dos programas. É esta
unidade que indica onde se encontra a próxima instrução e executa a presente. A unidade
aritmética e lógica é a responsável por todos os cálculos ou operações aritméticas (adição,
subtracção, etc.) e lógicas (E, OU, etc.). Por seu lado, os registos são os locais onde são
armazenados temporariamente os dados a processar (abusando ainda mais da linguagem,
funcionam como uma espécie de memórias especiais). Na Figura 13 pode ver-se num
esquema típico, a forma como estas unidades interagem entre si.
32
CPU ou Processador
Memória principal
BUS
Aquisição
Descodificação
Registos
Controlo
ALU
Periféricos
Figura 13 – Constituição típica de um CPU e de um sistema informático.
A Tabela 4 apresenta um quadro resumo da evolução de algumas das características principais
dos microprocessadores da família Intel.
Ano
Processador
N.º Transístores
Relógio (MHz)
Largura do Bus
Largura do Bus
de Endereços
de dados
Registos
Níveis de
Cache
1978 8086
29 mil
4,75
20 bit
16 bit
16 bit
-
1982 80286
134 mil
6-25
24 bit
16 bit
16 bit
-
1985 80386
275 mil
16-40
24 bit
32 bit
32 bit
-
1989 80486
1,2 milhões
25-100
32 bit
32 bit
32 bit
1 nível
1993 Pentium
3,1 milhões
60-233
32 bit
64 bit
32 bit
1 nível
1995 Pentium Pro 5,5 milhões
150-200
32 bit
64 bit
32 bit
2 níveis
1997 Pentium II
7,5 milhões
233-450
36 bit
64 bit
32 bit
2 níveis
1999 Pentium III
15 milhões
450-1000
36 bit
64 bit
32 bit
2 níveis
2001 Pentium 4
100 milhões 1000-3000 36 bit
64 bit
32 bit
2 níveis
Tabela 4 – Evolução de alguns parâmetros dos microprocessadores da família Intel.
33
4.4.3 – Memórias
As memórias podem ser divididas em dois grandes grupos: memórias primárias ou
principais; e memórias secundárias, de armazenamento, ou auxiliares. Por seu lado, as
memórias primárias dividem-se em memórias do tipo Read Only Memory (ROM) ou memória
de simples leitura, e memória do tipo Random Access Memory (RAM) ou memória de leitura
e escrita de acesso aleatório. No que se refere às memórias auxiliares, os principais tipos
actualmente em uso são os discos rígidos, disquetes, bandas magnéticas, CDs e DVDs. Na
Tabela 5 pode ver-se esta classificação de uma forma esquemática. Nas Figura 14, Figura 15 e
Figura 16 podem ver-se algumas fotografias de memórias típicas.
ROM
PROM
ROM
EPROM
Primárias
ou
EEPROM
principais
SRAM
RAM
DRAM
Discos
Memórias
Disquetes
Suportes de
armazenamento
secundário
Bandas magnéticas
CDs
DVDs
Tabela 5 – Tipos de memórias.
Figura 14 – Memória primária tipo ROM.
34
Figura 15 – Memória primária tipo RAM.
Figura 16 – Memória secundária, auxiliar ou de armazenamento tipo disco rígido.
As memórias primárias RAM e ROM de um computador são divididas em pequenas
unidades, todas do mesmo tamanho. Estas unidades são vulgarmente chamadas “palavras”,
tendo cada uma um endereço. Apesar dos seus endereços serem numerados de uma forma
ascendente (do mais baixo para o mais alto, começando em zero) os seus conteúdos podem
ser perfeitamente aleatórios, ou seja, o conteúdo de uma dada célula de memória não tem
qualquer relação com o seu endereço. Na Figura 17 ilustra-se este conceito e na Figura 18
pode ver-se um núcleo de memória de ferrite.
35
Endereço
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
Conteúdo
11010001
10101010
11110000
11001100
11010001
10101010
11110000
11001100
11010001
10101010
11110000
11001100
11010001
10101010
11110000
11001100
Figura 17 – Organização da memória.
Figura 18 – Núcleo de memória de ferrite.
As memórias secundárias permitem o armazenamento dos programas e informação que
os utilizadores necessitam de preservar para além dos momentos em que decorrem as
operações de processamento. Devemos distinguir entre dois tipos de dispositivos: os suportes
de armazenamento (discos, disquetes, etc.); e os que fazem circular a informação e que se
designam por drives. Nos dispositivos de armazenamento incluem-se os discos, as disquetes,
as bandas magnéticas, os discos ópticos (CD, DVD), os suportes magneto-ópticos (Discos de
Bernoulli e DVD-RAM ou DVD-RW), etcetera. Nas Figura 19 e Figura 20 mostra-se o
aspecto de duas superfícies de memórias de armazenamento secundário diferentes.
36
Figura 19 – Superfície de um disco rígido de 20 Mbytes.
Figura 20 – Superfície de um CD com música dos “Bon Jovi”.
4.4.4 – O barramento ou bus de um sistema informático
O barramento de sistema ou bus é constituído pelo conjunto de linhas ou ligações
eléctricas que permitem a sinalização e transferência de informação entre os diferentes
elementos constituintes do computador. Os sinais que circulam no bus são de três tipos, pelo
que é comum subdividir-se o bus em bus de dados, bus de endereços e bus de controlo – vejase a Figura 21.
37
BUS
CPU
Bus de endereços
Bus de dados
Bus de controlo
RAM/
ROM
Placas de
Expansão
I/O
Chips de
controlo
Figura 21 – Sistema de computador típico. Note-se a presença de três tipos de bus.
A arquitectura do bus define a maneira como este está concebido de forma a fazer a
interligação entre o CPU e os dispositivos de entrada/saída. Alguns dos tipos de arquitectura
de bus mais conhecidos são:
•
ISA – Industry Standard Architecture;
•
MCA – Micro Channel Architecture;
•
EISA – Extended ISA;
•
VL-BUS – VESA Local BUS;
•
PCI – Peripheral Component Interconnect;
•
PC Card ou PCMCIA – Personal Computer Module Cards International
Association;
•
USB – Universal Serial Bus;
•
AGP – Accelerated Graphics Port.
4.4.5 – Ligações de periféricos ou dispositivos de I/O
O CPU só produz trabalho útil se for capaz de “comunicar” com o exterior (porquê?).
Existe pois a necessidade da existência dos dispositivos de entrada/saída ou Input/Output
38
(I/O). Estes dispositivos ligam-se na sua maioria ao bus através de conectores localizados na
motherboard ou através das placas ou slots de expansão.
Neste grupo enquadra-se a placa de vídeo ou gráfica, que contém conectores para o
monitor, e as placas de utilização mais genérica, como é o caso das chamadas portas ou portos
(ports). Geralmente distingue-se entre portas série e portas paralelo. Esta distinção é devida à
forma como os dados são transmitidos: um bit de cada vez (transmissão série); ou vários bits
em simultâneo (transmissão paralelo).
Geralmente também se faz a distinção entre periféricos de entrada de dados, ou
simplesmente periféricos de entrada, periféricos de saída de dados, ou periféricos de saída, e
periféricos de entrada e saída de dados, ou periféricos de entrada/saída.
Alguns exemplos de dispositivos de saída são o monitor, a impressora e os traçadores
gráficos. Alguns dispositivos de entrada são o teclado o rato e o lápis óptico. As drives de
disquetes, os discos rígidos e os modems constituem exemplos de dispositivos de
entrada/saída. Passaremos agora a descrever de um modo muito resumido o funcionamento de
alguns destes dispositivos.
O monitor é um dispositivo de saída por excelência, que tradicionalmente é um tubo de
raios catódicos (Cathode Ray Tub – CRT). Num CRT, um feixe de electrões é disparado
através de sistemas de focagem e de deflexão sobre pontos específicos de uma superfície de
fósforo, emitindo um ponto luminoso. Veja-se a Figura 22. Na Figura 23 pode ver-se um
monitor típico.
Figura 22 – Esquema simplificado do modo de funcionamento de um CRT.
39
Figura 23 – Exemplo de CRT policromático (cores).
No entanto, este tipo de monitor tem vindo a ser substituído por outros, como por
exemplo:
•
Painel de cristal líquido (LCD) – o cristal líquido é composto por moléculas
cristalinas normalmente configuradas em espiral, fazendo rodar a polarização da
luz em 90º. Na presença de um campo eléctrico as moléculas alinham-se perdendo
este efeito polarizador;
•
Painel de Plasma – matriz de bolhas de néon em que cada uma pode ser activada
através de uma tensão;
•
Dispositivos 3D – baseando-se na característica estereoscópica da visão humana,
fornecem imagens diferentes a cada um dos olhos de forma a dar a ilusão de
tridimensionalidade;
•
Sistemas de projecção – permitem a projecção de imagens em superfícies
(normalmente) de grande dimensão.
As impressoras, vejam-se as Figura 24 e Figura 25, são geralmente classificadas quanto
ao seu modo de funcionamento e são normalmente divididas em quatro grandes grupos:
•
Matricial ou de agulhas – possui a cabeça de impressão com agulhas que actuam
(batem) individualmente sobre uma fita impregnada de tinta;
•
Laser – um feixe laser é direccionado para um tambor polarizando-o. O tambor
passa por toner que adere à parte atingida, sendo posteriormente pressionado
contra a folha de papel;
•
Jacto de tinta – a cabeça de impressão lança gotículas de tinta sobre o papel
(“jacto de tinta”);
40
•
Térmicas – as impressoras térmicas podem funcionar por dois métodos. A
impressão térmica directa e a impressão por transferência térmica. O sistema
térmico directo funciona com a cabeça térmica da impressora a aplicar calor sobre
um suporte (e.g. etiqueta) em papel térmico que tem a característica de escurecer
com o calor. É o mesmo método utilizado nas primeiras máquinas de fax. Este
sistema está limitado à utilização de suportes em papel térmico. Tem a vantagem
de ser mais económico ao nível dos consumíveis (só utiliza etiquetas), mas as
impressoras sofrem maior desgaste nas cabeças térmicas causado pelo contacto
directo com as etiquetas. O sistema de transferência térmica funciona transferindo
tinta do Foil para o suporte utilizado, i.e., o material receptor (e.g. etiqueta). O
Foil é o conjunto do Filme Base e a Tinta. O Filme Base serve de transporte da
Tinta e a sua qualidade fará com que o Foil não se rompa durante o processo de
impressão evitando paragens desnecessárias. Normalmente é de poliéster
transparente. Outra importante característica do Filme Base é a necessidade de ter
uma superfície suficientemente lisa e com baixo coeficiente de atrito para evitar
que a cabeça térmica da Impressora se danifique prematuramente, já que o
desgaste desta peça se deve basicamente aos efeitos abrasivos. No entanto, o
coeficiente de atrito do Foil é inferior ao causado pelas etiquetas em contacto
directo com a cabeça térmica, o que acontece no método impressão térmica
directa. Como regra importante, o Foil deve ser sempre mais largo do que o
material receptor. A Tinta é o ponto culminante do Foil e fabrica-se basicamente
em três formas: em base Cera, numa mistura de Cera/Resina, ou em base Resina.
O Foil é apresentado em bobinas de diferentes larguras e comprimentos para
adaptar-se às características das etiquetas e das impressoras.
Figura 24 – Exemplo de impressora “laser”.
41
Figura 25 – Exemplo de impressora “jacto de tinta”.
Alguns exemplos de traçadores gráficos (plotter) são:
•
Tipo cama – uma caneta desloca-se segundo dois eixos sobre um plano;
•
Tipo tambor – uma caneta desloca-se num eixo e a folha de papel num rolo
(tambor);
•
Tipo mesa – uma caneta desloca-se num eixo e a folha de papel permanece
imóvel na horizontal.
Vejam-se os exemplos das Figura 26 e Figura 27.
Figura 26 – Exemplo de traçador gráfico tipo tambor.
42
Figura 27 – Exemplo de traçador gráfico tipo mesa.
Os dispositivos de entrada por excelência são o teclado e o rato. Contudo existem muitos
outros que são de igual modo extremamente úteis. Na lista seguinte, e nas Figura 28, Figura
29 e Figura 30, apresentam-se alguns dispositivos de entrada de dados:
•
Teclado alfanumérico – é utilizado para a introdução de texto, comandos ou
selecção. Pode possuir conjuntos de teclas adicionais ou botões rotativos;
•
Rato (mouse) – detecta deslocamentos através da rotação da esfera na sua base
(ou de um feixe de luz) devido ao seu deslocamento num plano;
•
Painel táctil (touch panel) – identifica posições no ecrã através do toque do dedo;
•
Caneta ou lápis óptico (ligtht pen) – detecta posições no ecrã através da irradiação
do fósforo quando atingido pelo feixe de electrões;
•
Mesa digitalizadora – selecciona coordenadas 2D numa superfície;
•
Manípulo de controlo (joystick) – detecta deslocamentos através da inclinação do
manípulo;
•
Esfera localizadora (trackball) – detecta deslocamentos através da rotação da
esfera;
•
Scanners – permitem digitalizar imagens (formato raster) através do varrimento
óptico de desenhos ou figuras;
•
Digitalizador 3D – permite a medição de pontos num modelo físico;
43
•
Sensor Polhemos – usa a perturbação de campos magnéticos ortogonais causada
por um solenóide AC na caneta para o cálculo da sua posição e orientação no
espaço;
•
Spaceball – extensómetros medem a força aplicada na esfera segundo os três
ângulos de rotação;
•
Luva instrumentada – um sensor Polhemos indica a posição e orientação e um
sensor de flexão permite identificar a configuração de cada dedo da mão.
Figura 28 – Exemplo de teclado, rato, painel táctil e lápis óptico.
Figura 29 – Exemplo de mesa digitalizadora, joystick e trackball.
44
Figura 30 – Exemplo de digitalizador 3D, spaceball e luva instrumentada.
Os discos rígidos, disquetes e bandas magnéticas constituem o melhor exemplo de
dispositivos de entrada/saída. Contudo, existem outros exemplos, como sejam as placas de
som e de rede:
•
Placa de som – liga-se directamente num slot de expansão e oferece vários tipos
de conectores para o exterior, nomeadamente, mic in, line in, line out, speaker
out, joystick/ MIDI;
•
Placa de rede e modem – possibilitam a interligação de computadores, formandose redes de computadores (LAN, WAN, Internet, etc.).
Na Figura 31 apresenta-se um exemplo de uma placa de som e na Figura 32 um exemplo
de uma placa de rede.
Figura 31 – Exemplo de placa de som.
45
Figura 32 – Exemplo de placa de rede.
4.5 – Sistema Operativo
O sistema operativo (SO) é um dos componentes principais de um sistema informático.
Este cria um ambiente onde os utilizadores podem preparar os seus programas e executá-los
sem se preocuparem com os detalhes de hardware específicos de cada máquina. A
classificação do sistema informático segundo o número de tarefas simultaneamente permitidas
e o número de utilizadores simultâneos é determinado exclusivamente pelo tipo de sistema
operativo que este utiliza.
Entre outras tarefas de menor expressão, o SO faz a gestão do trabalho do processador, a
gestão da memória, a gestão de ficheiros e a gestão de periféricos. Uma das tarefas que os
sistemas operativos têm vindo a incorporar é a possibilidade de troca ou transferência de
informação entre aplicações diferentes. Na Figura 33 podem ver-se os componentes principais
de um sistema operativo típico.
A interface com o utilizador é geralmente de dois tipos: interface de linha de comando –
Command Line Interface (CLI); e interface gráfica – Graphical User Interface (GUI).
Alguns dos sistemas operativos mais divulgados são:
•
Disc Operating System ou simplesmente DOS (MS-DOS, PC-DOS, DR-DOS),
sendo mono–utilizador e mono–tarefa;
•
Unix (Unix SCO, AIX, HP-UX, Linux), sendo multi–utilizador e multi–tarefa;
•
Windows 3.x, 95, 98, 2000, NT, XP, sendo multi-utilizador e multi-tarefa.
46
Utilizador
Utilizador
SISTEMA
OPERATIVO
Editores
HARDWARE
Compiladores
CPU
Sistema
de
ficheiros
Memória
Periférico
Programas
Aplicacionais
Suporte para
comunicação
Loader
Utilizador
Processador de
Texto
Folha de Cálculo
Jogos
Browser
etc.
Figura 33 – Componentes principais de um sistema operativo.
Na organização da informação num SO deve distinguir-se pelo menos três níveis:
•
Drives, discos ou unidades de armazenamento – corresponde ao nível de entrada e
saída de dados ou de armazenamento;
•
Directorias, directórios ou pastas (folders) – correspondente à forma como os
dados se encontram organizados;
•
Ficheiros (files) – em última análise, é o local onde se encontram guardados os
dados.
Por sua vez, podemos dividir os ficheiros em duas classes principais:
•
Ficheiros de programas ou complementares de programas (ficheiros do tipo
executável, driver, etc.);
•
Ficheiros de dados (documentos de texto, imagens, bases de dados, etc.).
47
5 – Algoritmos
Antes de pensarmos em começar a programar um computador, devemos ter presente que
os computadores, feliz ou infelizmente, só fazem exactamente aquilo que mandamos e não
necessariamente o que desejamos que eles façam (pelo menos até à data!).
A programação de computadores pode ser difícil, mas podemos torná-la mais fácil se
dividirmos um problema sistematicamente em partes menos complexas. Esta técnica é
vulgarmente conhecida por “Dividir para conquistar”. Um problema mais ou menos complexo
é primeiramente dividido em problemas de dimensão mais reduzida e de fácil resolução.
Podemos fazer a analogia com um puzzle onde as pequenas peças se encaixam por forma
produzir a solução final (veja-se a Figura 34).
Figura 34 – Fases principais na resolução de um problema.
Na fase de resolução do problema, concentramo-nos apenas em elaborar um algoritmo
para resolver o problema proposto. Só depois é que passamos à fase de implementação do
algoritmo numa linguagem de programação. Tendo um algoritmo suficientemente preciso, a
sua codificação numa linguagem é directa.
Deve aprender-se a programar com uma e não numa linguagem de programação.
Um algoritmo pode ser definido como uma sequência ordenada, e sem ambiguidade, de
passos que levam à solução de um problema. Geralmente, descrevem soluções de problemas
do nosso mundo, afim de serem implementadas utilizando os recursos do mundo
computacional. Como este possui severas limitações em relação ao nosso mundo, exige que
49
sejam impostas algumas regras básicas na forma de solucionar os problemas, para que
possamos utilizar os recursos de hardware e software disponíveis. Pois, os algoritmos,
apesar de servirem para representar a solução de qualquer problema, no caso do
processamento de dados, eles devem seguir as regras básicas de programação para que sejam
compatíveis com as linguagens de programação.
Existem programas chamados de compiladores que traduzem os programas por nós
produzidos (escritos numa linguagem adequada à compreensão humana), para a linguagem
máquina (veja-se a Figura 35).
Programa
fonte
Compilação
Programa
Objecto
Execução
Resultados
Figura 35 – Fases de desenvolvimento/teste de um algoritmo/programa.
Pode definir-se programa fonte como sendo o conjunto de instruções escritas pelo
programador numa linguagem de programação, perceptível pelos humanos e vulgarmente
designada por “linguagem de alto nível” – por exemplo em PASCAL.
Pode também dizer-se que compilar um programa significa traduzi-lo do código fonte
para código objecto, que no fundo representa o código máquina (sequência de 0’s e 1’s).
As linguagens de programação podem ser classificadas em três grandes grupos ou tipos:
procedimentais, sequenciais ou imperativas; declarativas; e orientadas a objectos.
As linguagens tipo procedimentais, sequenciais ou imperativas distinguem-se por uma
sequência de comandos ou instruções fornecidas pelo programador onde o computador
executa mecanicamente uma série de ordens bem determinadas. O computador não tem
conhecimento do problema que está a resolver, sendo da responsabilidade do programador
descrever em pormenor a forma de resolução do problema. O PASCAL, C, Fortran e Basic
são exemplos de linguagens de programação deste tipo.
Por seu lado, nas linguagens de programação declarativas o programador descreve o
problema e uma série de relações entre as entidades, podendo o próprio computador
estabelecer o conjunto de acções para atingir a solução do problema. O programador não
especifica qualquer cálculo. Podem ser feitas perguntas relativas às relações sem ser preciso
explicar como se obtém a resposta. Estas linguagens são muito utilizadas na área da
Inteligência Artificial, constituindo o Prolog um exemplo por excelência.
50
Quanto às linguagens de programação orientadas a objectos podemos dizer que surgem
associadas à evolução da programação estruturada, clara e modular, sendo os seus módulos
independentes. As entidades são objectos, sendo estes compostos por dados e métodos que
operam sobre estes dados. Do conjunto de características destas linguagens destacam-se as
seguintes:
•
Encapsulamento;
•
Robustez;
•
Módulos reutilizáveis;
•
Classes;
•
Herança.
Como exemplos de linguagens de programação deste tipo podemos indicar o C++ e o JAVA.
5.1 – Noção de algoritmo
Um algoritmo deve descrever de forma detalhada e sem qualquer ambiguidade as
diferentes fases para a resolução de um problema. Tal como foi dito no ponto anterior, um
algoritmo é constituído por uma sequência ordenada, e sem ambiguidade, de passos que
levam à solução de um problema.
Nada melhor do que um exemplo para ajudar a clarificar estes conceitos, bem como
ajudar a definir aquilo que deve ser um algoritmo. Suponhamos que pretendemos substituir
uma lâmpada incandescente que se encontra fundida (esta é com certeza uma tarefa já
desempenhada por grande parte de nós). O nosso objectivo é descrever esta tarefa num
conjunto de passos de forma clara, ordenada e sem qualquer ambiguidade, que conduzam à
substituição da referida lâmpada. Podemos pois indicar o seguinte conjunto ordenado de
passos:
1. Colocar uma escada debaixo da lâmpada queimada;
2. Seleccionar uma nova lâmpada para a substituição;
3. Se a potência da lâmpada seleccionada não for igual à da lâmpada queimada,
repetir o processo até encontrar uma que sirva:
a. Descartar a lâmpada seleccionada;
51
b. Seleccionar uma outra lâmpada;
4. Repetir até chegar à lâmpada:
a. Subir um degrau na escada;
5. Repetir até que a lâmpada esteja desapertada do casquilho:
a. Girar a lâmpada no sentido anti-horário;
6. Colocar a nova lâmpada no casquilho;
7. Repetir até que a lâmpada fique apertada:
a. Girar a lâmpada no sentido horário;
8. Repetir até chegar ao solo:
a. Descer um degrau.
9. Guardar a escada.
Note-se que neste algoritmo existem passos que se podem considerar principais e outros
secundários. Isto não quer dizer que uns são mais importantes do que os outros. O que deve
ser realçado é o facto de os secundários só fazerem sentido porque os primários existem. Eles
servem como complemento de detalhe, ou se preferirmos, para melhor especificar o que se
pretende fazer com o passo principal. Repare-se que podem existir vários passos secundários
dentro de um principal. Por seu lado, dentro dos secundários também podem existir outros
sub-passos.
Uma das técnicas mais amplamente utilizada no desenvolvimento de algoritmos,
sobretudo algoritmos respeitantes a programas de grandes dimensões, é “dividir para
conquistar”. A ideia base consistem em dividir o problema global em problemas de menor
dimensão (sub-problemas) que possam, preferencialmente, ser resolvidos de forma autónoma
(isto é, cada um deles pode ser resolvido separadamente dos outros). No final a solução será
resultado do agrupamento dos diferentes sub-algoritmos, resultantes da resolução de cada um
dos sub-problemas.
5.2 – Dados e tipos de dados
Qualquer programa de computador manipula conjuntos de dados com o objectivo de
produzir informação.
52
Estes dados podem ser de vários tipos: numérico; caracter; cadeia de caracteres ou string;
enumerado; lógico; e ponteiro. Por seu lado, os dados do tipo numérico podem ser do tipo real
ou inteiro. Os dados do tipo real ainda se podem representar como decimais ou em vírgula
flutuante. Na Figura 36 pode ver-se um esquema em árvore da distribuição dos tipos de dados.
dados
numérico
real
decimal
caracter
string
enumerado
lógico
ponteiro
inteiro
vírgula-flutuante
Figura 36 – Tipos de dados.
Os dados numéricos do tipo inteiro não possuem parte fraccionária ou decimal, podendo
ser positivos ou negativos. Constituem exemplos de dados deste tipo os números 13, 7, -6,
208, 0 e 502.
Por seu lado, os dados do tipo real possuem uma parte inteira e uma parte fraccionária.
Os números 23.8, 0.02, -10.3, 12.0 e 502.36 são exemplo deste tipo de dados. Os números
representados em vírgula flutuante são incluídos no tipo real. Esta representação é usada para
valores numéricos muito grandes ou muito pequenos. Possuem um termo fraccionário
(mantissa) e um termo em expoente (expoente), geralmente como potência de 10. Os números
0.385x10+8 e 1.385x10-15 estão representados em vírgula flutuante.
Os dados não numéricos incluem todos os outros tipos de dados. Destes dados fazem
parte os do tipo caracter, conjunto de caracteres ou string, lógico, enumerado e ponteiro.
Os dados do tipo caracter são constituídos pelo conjunto padrão de caracteres. Tem-se os
caracteres alfabéticos, letras A, B, C, ..., Z, a, b, c, ..., z; os dígitos 0, 1, ..., 9; e os caracteres
especiais -, #, !, &, =, *, etcetera.
Os dados do tipo string ou cadeias de caracteres são formados por uma sequência de itens
do tipo caracter. Por exemplo ‘Bom dia’ e ‘77+23 =’. O delimitador de uma cadeia de
caracteres usada por nós é o símbolo apóstrofo ou aspa simples.
53
Os dados do tipo lógico ou Booleano (da álgebra de George Boole), só podem tomar dois
valores: falso (F) ou verdadeiro (V).
Não serão estudados os dados do tipo ponteiro.
5.3 – Operadores e operações básicas
Antes de podermos efectuar operações é conveniente definir os tipos de operadores com
que iremos trabalhar. Os operadores podem ser divididos em três grandes grupos: aritméticos;
lógicos; e relacionais.
No grupo de operadores aritméticos incluem-se os seguintes:
•
()
Parêntesis
•
+, –
Sinal
•
+
Adição
•
–
Subtracção
•
*
Multiplicação
•
/
Divisão.
A expressão (2+X)*Y+0.7*Z constitui um exemplo de utilização dos operadores
aritméticos. As regras de prioridade de aplicação destes operadores são as utilizadas na
análise matemática e serão clarificados no próximo ponto.
Os operadores relacionais são formados por:
•
≤
Menor ou igual
•
<
Menor
•
≥
Maior ou igual
•
>
Maior
•
=
Igual
•
≠
Diferente.
A expressão x ≥ 10 constitui um exemplo de utilização dos operadores relacionais.
54
Os operadores lógicos que iremos ver são constituídos pela conjunção ‘E’, disjunção
‘OU’, ‘OU EXCLUSIVO’ e pela negação ‘NEGAÇÃO’. A expressão (x = 10) E (y > 0)
constitui um exemplo de utilização dos três tipos de operadores.
5.4 – Variáveis e expressões
A noção mais importante na definição de um algoritmo é a de variável. Esta noção é
muito semelhante à de variável utilizada na análise matemática. Contudo, existem certas
particularidades que de uma forma geral estão relacionadas com a capacidade de
representação, e esta por seu lado está fortemente ligada à linguagem de implementação
utilizada. Por estes motivos não entraremos em grandes detalhes nem definições, optando por
clarificar os conceitos com recurso a exemplos.
Contudo, não podemos deixar de dar uma definição, ainda que superficial do que é uma
variável. Uma variável é uma entidade que possui um valor, sendo reconhecida no programa
por um nome (identificador). Pode receber diferentes valores ao longo de um algoritmo ou
programa, mas num determinado instante só guarda um valor. O nome de uma variável deve
começar obrigatoriamente por uma letra. Os restantes caracteres podem ser letras ou dígitos,
não sendo permitidos espaços. Por motivos de pormenor de implementação, aconselhamos a
não utilização de qualquer acentuação na atribuição de nomes (identificadores) às variáveis.
Por outro lado, uma constante é uma entidade que armazena um valor definido numa
expressão literal de um determinado tipo. Este valor não pode ser alterado por qualquer
declaração ao longo do algoritmo ou programa. Uma constante também é representada por um
identificador, obedecendo a sua construção (o seu nome) às mesmas regras das variáveis. A
constante matemática número irracional π (de valor aproximado 3.1415) constitui um
exemplo de definição de constante (num algoritmo ou programa usa-se geralmente o
identificador ‘PI’ para representar esta constante).
A operação de atribuição tem por objectivo colocar um dado valor numa variável, isto é,
a partir daquele ponto, e até que uma operação idêntica aconteça, essa variável passa a
guardar aquele valor. Na sua forma geral, esta operação possui a sintaxe:
variável ß expressão.
Note-se que esta operação é destrutiva, isto é, perde-se o valor que a variável guardava
anteriormente. Por exemplo, suponhamos um algoritmo com as seguintes instruções:
55
Aß3
A ß A*2
Com a primeira operação de atribuição a variável A passa a guardar o valor 3. Ao executar a
segunda operação o valor 3 é perdido e passa a guardar o valor 6 (correspondente a
multiplicar o valor anteriormente guardado 3 pela constante 2).
Note-se que numa operação de atribuição só é alterado o valor da variável do lado
esquerdo da atribuição. Os valores das variáveis que possam existir do lado direito mantêm-se
inalteráveis (caso não existam do lado esquerdo).
Uma expressão resulta de uma combinação de variáveis, constantes e operadores. Por
exemplo:
c ß a/b
area ß base*altura/2
Para ajudar a clarificar os conceitos até agora apresentados e as regras de prioridade dos
operadores aritméticos, vejam-se os seguintes exemplos:
1. A ß 3+6*2
2. A ß -5-6*5/6
3. A ß 8+7*3+4*5
4. A ß (3+6)*2
Tendo presente que a multiplicação e a divisão tem prioridade sobre a adição e a subtracção e
que a utilização de parêntesis introduz prioridade máxima, os resultados de cada uma das
expressões anteriores é:
1. 15
2. -10
3. 49
4. 18.
56
5.5 – Entrada e saída de dados
Tal como já anteriormente notado, os computadores só produzem trabalho útil se lhe
podermos fornecer dados e se ele for capaz de nos devolver o resultado das operações
efectuadas sobre os dados. Existem instruções específicas para este efeito.
A instrução LER(lista de entrada) tem por objectivo a introdução de dados pelo teclado,
isto é, o operador pode fornecer um conjunto de dados ao computador (programa) via teclado.
Podem ser indicados vários valores a ser introduzidos numa única instrução do tipo LER,
separados por vírgula, ou apenas um valor. Por exemplo:
LER(raio)
deve ser introduzido pelo teclado o valor para a variável raio.
LER(A, B, C)
devem ser introduzidos pelo teclado os valores para as variáveis A, B
e C.
Por outro lado, quando se pretende apresentar os resultados ao operador, existe a
instrução ESCREVER(lista de saída). Esta instrução apresenta no écran o conjunto de dados
constantes na lista. Por exemplo:
ESCREVER(A, B, C)
apresenta no écran os valores das variáveis A, B e C.
ESCREVER('3+4=', C)
apresenta no écran a frase 3+4= e o valor da variável C.
5.6 – Funções embutidas ou pré-definidas
Existem funções previamente programadas, e que geralmente se designam por embutidas
ou pré-definidas, que são particularmente úteis para cálculo aritmético. Na Tabela 6
apresentamos algumas dessas funções. Desta tabela consta também o tipo de dado que a
função aceita (inteiro ou real) e o valor devolvido (o resultado do cálculo e o seu tipo de
dados associado – real ou inteiro).
57
Nome da
função
ABS(x)
SQRT(x)
SQR(x)
TRUNC(x)
ROUND(x)
LOG(x)
LOG10(x)
EXP(x)
SIN(x)
COS(x)
Tipo de dado que aceita
x: uma expressão real ou inteira
x: uma expressão real ou inteira
(deve ser ≥ 0)
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
x: uma expressão real ou inteira
Valor devolvido
Valor absoluto: o resultado é |x|, mesmo tipo que x
Raiz quadrada: Resultado é
x , tipo real
O valor do quadrado de x, ou seja, x2
Valor truncado: o resultado é o maior inteiro T tal que
|T| ≤ |x|
Valor arredondado: o resultado é o maior inteiro T tal que
|T| ≤ |x+0.5|
Logaritmo base e: o resultado é o logaritmo neperiano ou
natural de x, tipo real
Logaritmo base 10: o resultado é o logaritmo base 10, de x,
tipo real
x
Exponencial: o resultado é e , tipo real
Seno: o resultado é o seno de x (radianos), tipo real
Coseno: o resultado é o coseno de x (radianos), tipo real
Tabela 6 – Exemplos de funções pré-definidas.
58
6 – Estruturas de decisão
Com as ferramentas até ao momento apresentadas já somos capazes de produzir
algoritmos simples. Estes algoritmos seguem sensivelmente um modelo padrão:
1. Entrada de dados
2. Cálculos
3. Apresentação de resultados.
Vamos agora apresentar dois poderosos conceitos de programação, os quais requerem o
poder de decisão e através dos quais podemos alterar o fluxo linear de um programa pelo uso
de estruturas de controlo.
As estruturas a estudar neste ponto permitem seleccionar uma acção de um conjunto de
alternativas especificadas. No próximo ponto estudaremos estruturas que permitem repetir
uma série de acções, que vulgarmente denominadas de repetitivas. Ambas fazem parte das
estruturas de decisão e controlo.
6.1 – Selecção de acções alternativas – SE-ENTÃO-SENÃO
A construção algorítmica SE-ENTÃO-SENÃO permite-nos seleccionar entre duas acções
alternativas, permitindo a execução de partes distintas do algoritmo. O formato desta
instrução é o seguinte:
SE condição ENTÃO
alternativa verdadeira
SENÃO
alternativa falsa
FIM SE
Nesta construção, a parte do algoritmo “alternativa verdadeira” será executada caso a
condição “condição” se verifique, isto é, caso esta condição seja verdadeira. Caso contrário (a
condição não se verifica) é executada a parte do algoritmo correspondente à “alternativa
falsa”. Esta construção possui a interpretação gráfica apresentada na Figura 37, que
corresponde ao diagrama de fluxo ou simplesmente fluxograma.
59
Entrada
Verdadeira
Falsa
Avaliar
condição
Processar alternativa
verdadeira
Processar alternativa
falsa
Saída
Figura 37 – Diagrama de fluxo da construção SE-ENTÃO-SENÃO.
Esta construção possui uma forma simplificada em que a alternativa falsa é vazia (não
existe). Esta versão da instrução possui a forma SE-ENTÃO, devendo ser utilizada sempre
que desejarmos fazer algo apenas quando a condição for verdadeira. Na Figura 38 pode ser
visto o diagrama de fluxo correspondente.
Entrada
Verdadeira
Avaliar
condição
Falsa
Processar alternativa
verdadeira
Saída
Figura 38 – Diagrama de fluxo da construção SE-ENTÃO.
Veja-se um exemplo de aplicação da estrutura SE-ENTÃO-SENÃO. Suponhamos que
pretendemos determinar qual de dois números introduzidos via teclado é o maior, escrevendoo no écran. Não nos vamos preocupar com o tipo de número, isto é, não interessa se os
números são inteiros ou reais. Na Figura 39 apresenta-se o diagrama de fluxo correspondente
à parte fundamental do algoritmo. As variáveis A e B representam os dois números.
60
Entrada
Verdadeira
Falsa
A>B?
Escrever (B)
Escrever (A)
Saída
Figura 39 – Diagrama de fluxo para determinação do maior de dois valores.
Em linguagem algorítmica, este segmento escreve-se
SE A > B ENTÃO
ESCREVER(A)
SENÃO
ESCREVER(B)
FIM SE
Em PASCAL escreve-se
IF A > B THEN
WRITE(A)
ELSE
WRITE(B)
NOTA: a linguagem de programação PASCAL não distingue entre maiúsculas e minúsculas.
Pode-se escrever o programa utilizando qualquer combinação destes dois tipos de letras. Por
exemplo, AREA, Area, aREA, etc., em PASCAL serão considerados o mesmo identificador.
Contudo, existem linguagens que fazem esta distinção, como é o caso do C. Nos exemplos
que se apresentam ao longo destes apontamentos é pois possível encontrar estas situações.
Vejamos um exemplo completo. Pretende-se determinar se um dado aluno foi ou não
aprovado a uma disciplina. Para isso é necessário introduzir via teclado a respectiva
classificação, apresentando-se depois o resultado no écran. Se o aluno ficou aprovado, deve
61
ser apresentada a nota final arredondada à unidade. Se o aluno reprovou apenas deve ser
apresentado no écran este facto. No algoritmo seguinte apresenta-se uma solução para o
problema:
ALGORITMO Classificação
VARIÁVEIS nota DO TIPO REAL
1. [Ler a nota do aluno]
ESCREVER('Classificação no teste: ')
LER(nota)
2. [Classificar o aluno]
SE nota ≥ 9.5 ENTÃO
ESCREVER('Aprovado com ', ROUND(nota), ' valores.')
SENÃO
ESCREVER('Reprovado')
FIM SE
3. FIM DO ALGORITMO
Ao algoritmo é atribuído um nome que deve procurar clarificar a sua finalidade, ou seja, que
resuma o seu objectivo principal. Repare-se que a seguir à declaração ‘ALGORITMO
classificação’ surge a zona de declaração de variáveis. Neste caso, a variável ‘nota’ serve para
guardar a classificação introduzida pelo teclado. Esta variável deve ser do tipo real uma vez
que a classificação do aluno pode conter parte decimal. Note-se que antes de se ler o valor da
classificação do aluno apresenta-se uma mensagem informativa para que o operador saiba o
que está a acontecer. A função pré-definida ROUND é responsável por efectuar o
arredondamento à unidade na classificação final (veja-se a Tabela 6). A correspondente
codificação em PASCAL é a seguinte:
Program Classificacao;
Var nota : Real;
Begin
{Ler a nota do aluno}
62
Write('Classificação no teste: ');
Read(nota);
{Classificar o aluno}
If nota >= 9.5 then
Writeln('Aprovado com ', Round(nota), ' valores.')
Else
Writeln('Reprovado');
End.
Nesta codificação repare-se em particular que o símbolo ‘≥’ é substituído por ‘>=’, os
comentários são introduzidos entre chavetas e no aparecimento de um ponto–e–vírgula a
separar cada instrução. Note-se ainda que o final do programa é marcado com a palavra ‘end’
seguida de ponto–final. Antes da palavra ‘else’ não é introduzido nenhum ponto-e-vírgula
porque a instrução ‘if-then-else’ só acaba no fim do ‘else’. Não deveríamos ter colocado um
ponto–e–vírgula no final desta construção, uma vez que a instrução que se segue é um ‘end’.
Contudo, este tipo de falha tem por consequência a introdução da instrução máquina NOP (no
operation), que corresponde a não haver qualquer operação por parte do processador. Neste
caso o objectivo da sua utilização é ajudar a clarificar a forma como se define a estrutura ‘ifthen-else’.
6.2 – SE’s encaixados
É possível incluir dentro de uma estrutura do tipo SE-ENTÃO-SENÃO outra estrutura do
mesmo tipo. Este tipo de inclusão é vulgarmente conhecido pela denominação de “encaixe”.
Expliquemos o formato com recurso a um exemplo:
SE c1 ENTÃO
s1
SENÃO
SE c2 ENTÃO
s2
SENÃO
63
s3
FIM SE
FIM SE
Repare-se que ‘s3’ só será executada (ou atingida) caso as condições ‘c1’ e ‘c2’ sejam falsas
(porquê?). Paralelamente à utilização de estruturas SE-ENTÃO-SENÃO encaixadas, é
também possível utilizar a conjunção (E) e a disjunção (OU) de condições. Isto aumenta a
flexibilidade de escrita, permitindo reduzir o número de estruturas do tipo SE-ENTÃOSENÃO e simplificar a escrita de algoritmos. No algoritmo seguinte ilustra-se a aplicação de
estruturas SE-ENTÃO-SENÃO encaixadas.
Numa Operação Stop típica no IP4, quando uma viatura circular a uma velocidade
superior a 90 Km/h deve ser mandada parar. A infracção é considerada grave se o excesso de
velocidade for inferior ou igual a 30 Km/h e muito grave em caso contrário. O algoritmo
seguinte apresenta uma solução para este problema.
ALGORITMO Operação stop IP4
VARIÁVEIS matricula DO TIPO STRING
velocidade, excesso DO TIPO REAL
1. [Definir limite de velocidade]
CONSTANTE LIMITE = 90
2. [Ler a matricula do automóvel]
ESCREVER('Matricula: ')
LER(matricula)
3. [Ler a velocidade do automóvel]
ESCREVER('Velocidade: ')
LER(velocidade)
4. [Multar o veículo em caso de excesso de velocidade]
SE velocidade > LIMITE ENTÃO
ESCREVER('Fazer parar o automóvel ', matricula)
excesso ß velocidade-LIMITE
64
ESCREVER('Excesso de velocidade de ',excesso,' Km/h.')
SE excesso ≤ 30 ENTÃO
ESCREVER('Passar multa grave.')
SENÃO
ESCREVER('Passar multa muito grave.')
FIM SE
FIM SE
5. FIM DO ALGORITMO
Note-se que antes de poder decidir se a viatura transita ou não em excesso de velocidade é
necessário conhecer a sua matrícula e a velocidade respectiva. A uma viatura só se passa uma
multa muito grave se antes de mais transitar em excesso de velocidade (daí o teste para
determinar se a multa é muito grave ser feito apenas quando já se sabe que a viatura circula
em excesso de velocidade). Repare-se ainda que caso a viatura circule dentro dos limites de
velocidade estipulados pela lei, nenhuma acção deve ser desenvolvida. A correspondente
codificação em PASCAL é a que se segue:
program Operacao_stop_IP4;
const LIMITE=90;
var
velocidade, excesso : Real;
matricula: string;
begin
{Ler a matricula do automóvel}
write('Matricula: ');
readln(matricula);
{Ler a velocidade do automóvel}
write('Velocidade: ');
readln(velocidade);
{Multar o veículo em caso de excesso de velocidade}
65
if velocidade > LIMITE then
begin
writeln('Fazer FIM DO ALGORITMO o automóvel ', matricula);
excesso := velocidade-LIMITE;
writeln('Excesso de velocidade de ',excesso,' Km/h.');
If excesso <= 30 then
writeln('Passar multa grave.')
else
writeln('Passar multa muito grave.');
end
end.
Nesta codificação repare-se na utilização da construção ‘begin-end’ dentro da estrutura ‘ifthen’. Esta estrutura possibilita executar um conjunto de instruções numa situação em que
aparentemente só era possível executar apenas uma. Esta construção é geralmente designada
por “bloco de código”.
6.3 – Estrutura de selecção múltipla
Quando temos muitas construções do tipo SE-ENTÃO-SENÃO encaixadas o algoritmo
torna-se confuso. A estrutura CASO permite fazer uma selecção quando temos várias
alternativas. O formato desta estrutura é o seguinte:
CASO (expressão) POSSUA VALOR
valor v1 : Lista de declarações
valor v2 : Lista de declarações
valor vn : Lista de declarações
valor i1 até valor i2 : Lista de
declarações
valor s1, valor s2 , .., valor sn : Lista de declarações
defeito: Lista de declarações
66
FIM CASO
Suponhamos que pretendíamos determinar se um dado caracter introduzido via teclado
corresponde a uma letra ou a um dígito. Uma solução em PASCAL é
case opcao of
‘A’..’Z’, ‘a’..’z’: writeln(‘Letra’);
‘0’..’9’: writeln(‘Digito’);
else writeln(‘Caracter especial’)
end
Note-se a existência de um ‘else’ fazendo lembrar precisamente a estrutura ‘if-then-else’.
No exemplo que se segue pretende-se simular o funcionamento de uma calculadora com
as operações de adição, subtracção, multiplicação e divisão. Em primeiro lugar deve ser
indicado o primeiro operando, seguido da operação e terminando com o segundo operando.
No final devem ser apresentados o primeiro operando, a operação realizada, o segundo
operando e o resultado. Caso a operação indicada não conste da lista de operações
implementadas deve ser apresentada uma mensagem de erro esclarecedora. O seguinte
algoritmo apresenta uma solução para este problema.
ALGORITMO Calculadora
VARIÁVEIS op1, op2 DO TIPO REAL
operacao DO TIPO CARACTER
1. [Ler o valor do primeiro operando]
ESCREVER('1º Operando: ')
LER(op1)
2. [Seleccionar a operação a realizar]
ESCREVER('Adição(+), Subtracção(-), Multiplicação(*) e Divisão(/)')
LER(operacao)
3. [Ler o valor do segundo operando]
ESCREVER('2º Operando: ')
67
LER(op2)
4. [Escrever o resultado da operação seleccionada]
CASO (operacao) POSSUA VALOR
'+': ESCREVER(op1,'+',op2,'=',op1+op2)
'-': ESCREVER(op1,'-',op2,'=',op1-op2)
'*': ESCREVER(op1,'*',op2,'=',op1*op2)
'/': ESCREVER(op1,'/',op2,'=',op1/op2)
DEFEITO: ESCREVER('Operação ilegal')
FIM CASO
5. FIM DO ALGORITMO
Note-se que as variáveis ‘op1’ e ‘op2’ guardam os valores dos operandos, sendo estas do
tipo real (porque os valores a introduzir podem ser deste tipo). A variável ‘operacao’ é do tipo
caracter, uma vez que se pretende guardar o valor de um dos símbolos ‘+’, ‘-’, ‘*’, ‘/’. A
codificação em PASCAL é a seguinte:
program calculadora;
var op1, op2 : real;
operacao : char;
begin
{Ler o valor do primeiro operando}
write('1º Operando: ');
readln(op1);
{Seleccionar a operação a realizar}
writeln('Adição(+),Subtracção(-),Multiplicação(*) e Divisão(/)');
readln(operação);
{Ler o valor do segundo operando}
write('2º Operando: ');
68
readln(op2);
{Escrever o resultado da operação seleccionada}
case operacao of
'+' : writeln(op1,'+',op2,'=',op1+op2);
'-' : writeln(op1,'-',op2,'=',op1-op2);
'*' : writeln(op1,'*',op2,'=',op1*op2);
'/' : writeln(op1,'/',op2,'=',op1/op2);
else writeln(‘Operação ilegal’)
end
end.
O que acontece quando o segundo operando for nulo e a operação escolhida a divisão? Como
exercício sugerimos a alteração do algoritmo de forma a evitar este problema.
A diferença principal entre a instrução WRITE e a instrução WRITELN reside no facto
da última mudar de linha após ter escrito no écran. A instrução READLN faz um reset ao
buffer de entrada antes de ler do teclado, enquanto que a instrução READ não faz. Na prática,
sempre que pretendermos ler vários valores consecutivos via teclado devemos usar a instrução
READLN.
69
7 – Ciclos
Neste ponto abordaremos as estruturas de decisão repetitivas, isto é, estruturas que
dependendo da verificação ou não de uma dada condição executam de forma repetitiva um
conjunto de instruções. Dada a sua arquitectura, os computadores são particularmente
adequados a aplicações nas quais uma operação ou uma série de operações devem ser
repetidas várias vezes.
Existem dois tipos de estruturas deste tipo. As estruturas que repetem um conjunto de
operações (ou instruções) um número fixo e bem determinado de vezes – ciclos
incondicionais – e as estruturas cujo número de vezes que repetem as operações dependendo
da verificação de uma condição – ciclos condicionais.
7.1 – Estrutura REPETIR-ENQUANTO
A estrutura REPETIR-ENQUANTO (while-do) é uma estrutura repetitiva condicional. O
seu formato é o seguinte:
REPETIR ENQUANTO condição
Lista de declarações
FIM REPETIR
Note-se que a ‘lista de declarações’ só é executada se a condição for verdadeira. Este tipo de
estrutura deve ser usada sempre que exista a possibilidade de a condição não se verificar logo
à partida. Uma aplicação típica é o das listas de dados que podem ser vazias. A ‘lista de
declarações’ deve conter instruções que possam modificar a condição (sob pena de se entrar
num ciclo infinito). Na Figura 40 pode ver-se o diagrama de fluxo correspondente.
71
Entrada
Avaliar a
Condição de
Ciclo
Falsa
Verdadeira
Processar informação
interior ao ciclo
Saída
Figura 40 – Diagrama de fluxo da estrutura REPETIR-ENQUANTO.
Suponha-se que se pretende ler uma série de valores via teclado e somá-los. Esta lista
termina com a introdução do valor – 1, podendo esta série de valores ser vazia, isto é, o
primeiro elemento a ser introduzido pode ser o valor – 1. No algoritmo seguinte apresenta-se
uma solução para o problema:
soma ß 0
LER(x);
REPETIR ENQUANTO x ≠ –1
soma ß soma + x
LER(x)
FIM REPETIR
A variável ‘x’ serve para guardar o valor introduzido pelo teclado. Note-se a utilização da
variável ‘soma’ para acumular o resultado total. Antes de se poder adicionar algo a esta
variável, torna-se necessário garantir que o seu conteúdo é nulo. Isto é feito através da
utilização da instrução ‘somaß0’. Este é um princípio muito utilizado e designado por
inicialização de variáveis. Repare-se ainda que o valor ‘-1’ não é incluído na soma final
(total). A seguir apresenta-se a codificação em PASCAL correspondente:
soma := 0;
read(x);
while x <> –1 do
72
begin
soma := soma + x;
read(x)
end;
Note-se uma vez mais a utilização do bloco de código (instruções begin-end) dentro da
estrutura while-do (REPETIR-ENQUANTO) com o objectivo de tornar possível o executar de
um conjunto de instruções onde se esperava apenas uma.
No algoritmo seguinte, apresenta-se uma solução para o cálculo do factorial de um
número. A variável ‘numero’ guarda o número do qual se pretende calcular o factorial, a
variável ‘factor’ serve para contar desde 2 até ao número introduzido (controlando-se desta
forma a saída do ciclo), e a variável ‘resultado’ guarda o resultado final.
ALGORITMO Factorial
VARIÁVEIS numero, factor, resultado DO TIPO INTEIRO
1. [Ler numero para o qual deve ser calculado o factorial]
ESCREVER('Numero: ')
LER(numero)
2. [Calcular o factorial: n!=1*2*...*n]
resultado ß 1
factor ß 2
REPETIR ENQUANTO factor ≤ numero
resultado ß resultado * factor
factor ß factor + 1
FIM REPETIR
3. [Escrever o resultado]
ESCREVER(numero, '! = ',resultado)
4. FIM DO ALGORITMO
A codificação em PASCAL correspondente é:
73
program Factorial;
var resultado, numero, factor : integer;
begin
{Ler numero para o qual deve ser calculado o factorial}
write('Numero: ');
read(numero);
{Calcular o factorial: n!=1*2*...*n}
resultado := 1;
factor := 2;
while factor <= numero do
begin
resultado := resultado * factor;
factor := factor + 1
end;
{Escrever o resultado}
writeln(numero, '! = ',resultado)
end.
Neste momento impõe-se uma nota acerca da capacidade de representação das variáveis
do tipo ‘integer’ em PASCAL numa máquina com sistema operativo Windows. As variáveis
deste tipo são capazes de representar números inteiros na gama de valores compreendidos
entre –32768 a +32767. Como é sabido o factorial de 8 é 40320, excedendo a capacidade de
representação deste tipo de variável. Se pretendermos calcular o factorial de um número
superior a 7 devemos pois usar outro tipo de variável, por exemplo long int (consulte-se o
manual da distribuição do PASCAL).
7.2 – Estrutura REPETIR-ATÉ
A outra estrutura repetitiva condicional que vamos estudar é a REPETIR-ATÉ (repeatuntil), que possui a seguinte estrutura:
74
REPETIR
Lista de declarações
ATÉ condição
Note-se que a ‘lista de declarações’ é executada pelo menos uma vez, independentemente da
condição ser verdadeira ou falsa. Uma aplicação típica é o das listas de dados não vazias.
Uma vez mais, a ‘lista de declarações ’ deve conter instruções que possam modificar a
condição (sob pena de se entrar num ciclo infinito). Repare-se que a lista de declarações será
executada até que a condição seja verificada, isto é, quando a condição se verificar o ciclo
termina. Na Figura 41 pode verse o diagrama de fluxo correspondente.
Entrada
Processar informação
interior ao ciclo
Avaliar a
Condição de
Ciclo
Verdadeira
Falsa
Saída
Figura 41 – Diagrama de fluxo da estrutura REPETIR-ATÉ.
Ilustremos a utilização desta estrutura recorrendo ao exemplo anteriormente apresentado:
calcular a soma de um conjunto de números introduzidos pelo teclado e terminada pela
introdução do número –1. Desta vez a lista de números introduzidos não pode ser vazia, isto
é, o primeiro elemento não pode ser o número –1. O algoritmo seguinte apresenta a
componente principal de uma solução para o problema:
soma ß 0
REPETIR
LER(x)
soma ß soma + x
ATÉ x = –1
75
Note-se a negação da condição de paragem do ciclo (na estrutura REPETIR-ENQUANTO era
x ≠ –1 e agora é x = –1). A seguir apresenta-se a respectiva codificação em PASCAL:
soma := 0;
repeat
read(x);
soma := soma + x,
until x = –1
Repare-se que neste caso não é necessário utilizar as instruções begin-end para criar um bloco
de código. Verifique-se ainda que o valor ‘-1’ é somado ao resultado final. Como alteraria o
algoritmo de forma a não ser incluído?
É possível transformar uma estrutura do tipo REPETIR-ENQUANTO numa estrutura do
tipo REPETIR-ATÉ (fazendo com que a condição de controlo se verifique à primeira vez,
utiliando uma inicialização conveniente desta variável). O contrário é impossível.
O algoritmo seguinte ilustra uma vez mais a utilização desta estrutura através de uma
implementação do jogo da adivinha de um número entre 1 e 100.
ALGORITMO Adivinha
VARIÁVEIS n, numero, x DO TIPO INTEIRO
1. [Gerar um número aleatório entre 1 e 100]
numero ß GeraNumeroAleatorio(100)
2. [Ler numero do utilizador até adivinhar]
ESCREVER('Adivinhe um numero entre 1 e 100:')
nß1
REPETIR
ESCREVER(n, ': ')
LER(x)
SE x > numero ENTÃO
ESCREVER('Errou: Numero muito grande.')
76
SENÃO
SE x < numero ENTÃO
ESCREVER('Errou: Numero muito pequeno.')
SENÃO
ESCREVER('Acertou no número ',numero, ‘ em ',n, ' tentativas.')
FIM SE
FIM SE
nßn+1
ATÉ x = numero
3. FIM DO ALGORITMO
As variáveis ‘n’, ‘numero’ e ‘x’ servem para guardar o número de tentativas, o número a
adivinhar e o valor da tentativa actual, respectivamente. Repare-se que o processo aleatório de
escolha do número entre 1 e 100 é automático. Isto é devido ao facto de na maioria das
linguagens de programação existirem instruções que o permitem fazer de uma forma simples
e eficiente. Como se pode ver no código seguinte, em PASCAL, estas instruções são
‘randomize’ para inicialização do sistema gerador, e ‘random’ que retorna um número
aleatório entre 0 o número apresentado entre parêntesis.
program Adivinha;
var numero, x, n: integer;
begin
{Gerar um número aleatório entre 1 e 100}
randomize;
numero := random(100)+1;
{Ler numero do utilizador até adivinhar}
writeln('Adivinhe um numero entre 1 e 100:');
n := 1;
repeat
77
write(n, ': ');
readln(x);
if x > numero then
writeln('Errou: Numero muito grande.')
else
if x < numero then
writeln('Errou: Numero muito pequeno.')
else
writeln('Acertou no numero ',numero, ' em ',n, 'tentativas.');
n := n + 1;
until x = numero
end.
7.3 – Entrada de dados através de ciclos
Como foi visto nos exemplos anteriores, os ciclos constituem um meio por excelência
para a entrada condicional de dados. Na disciplina apenas abordaremos a entrada de dados via
teclado, mas os princípios aqui apresentados são válidos para a entrada de dados através de
ficheiros ou outros dispositivos de entrada. Tal como já foi dito anteriormente, as listas de
dados podem ser ou não garantidamente não vazias. Dependendo da situação, usaremos
algoritmos baseados na estrutura REPETIR-ENQUANTO (as listas podem ser vazias) ou na
estrutura REPETIR-ATÉ (listas de dados não vazias). Os diagramas de fluxo apresentados na
Figura 42 e na Figura 43 mostram-nos os principais algoritmos.
78
Entrada
Inicializar a variável de
controlo para o primeiro
elemento da lista
Não
O valor actual da
variável de
controlo de ciclo
é maior do que o
valor final
estabelecido?
Processar as instruções
interiores ao ciclo
Sim
Afectar a variável de controlo
de ciclo com o próximo
elemento da lista
Figura 42 – Diagrama de fluxo para entrada de dados (lista não vazia).
Entrada
Inicializar a variável de
controlo para o primeiro
elemento da lista
O valor actual da
variável de
controlo de ciclo
é maior do que o
valor final
estabelecido?
Afectar a variável de controlo
de ciclo com o próximo
elemento da lista
Sim
Processar as instruções
interiores ao ciclo
Não
Figura 43 – Diagrama de fluxo para entrada de dados (lista vazia).
7.4 – Ciclos contados
A última estrutura repetitiva que vamos abordar é a REPETIR-PARA. Neste tipo de
estrutura o conjunto de instruções/operações é repetido um número fixo de vezes, número este
79
estabelecido à entrada no ciclo. Contrariamente ao que acontecia para as estruturas REPETIRENQUANTO e REPETIR-ATÉ, aqui não é testada nenhuma condição a fim de determinar se
o conjunto de operações interiores ao ciclo é ou não efectuado.
Para as estruturas REPETIR-ENQUANTO e REPETIR-ATÉ as instruções no intervalo
são repetidos tantas vezes quantas a condição expressa for verdadeira ou falsa. Esta condição
pode ser a avaliação de um valor que é lido dentro do ciclo (neste caso estamos na presença
de ciclos condicionais controlados por entrada). A condição pode também ser uma
composição de condições, utilizando os operadores negação, conjunção e disjunção.
Por seu lado, nos ciclos contados as instruções no intervalo são repetidas um número de
vezes bem determinado, dado pela expressão (|valor_inicial – valor_final | div y) + 1, onde y
representa o valor a somar ou a descontar em cada repetição do ciclo. Estes ciclos possuem o
seguinte formato
REPETIR PARA indice ß valor_inicial ATÉ valor_final COM PASSO y
lista_declarações
FIM REPETIR
Repare-se que é atribuído à variável ‘indice’ um valor inicial valor_inicial através de uma
operação de atribuição. A este valor será adicionado o valor de y até ser atingido o valor final
valor_final. O conjunto de operações constantes da lista de declarações será executado um
número de vezes fixo, determinado pelos valores valor_inicial, valor_final e y. Repare-se
ainda que y pode ser um valor negativo. Sempre que o passo assuma o valor 1 pode ser
omitida a sua declaração, isto é, se não se disser nada a variável y assume o valor 1. A
variável de controlo de ciclo ‘indice’ pode ser usada em expressões dentro da lista de
declarações, mas o seu valor não deve ser alterado, uma vez que é esta variável quem
determina a paragem ou continuação da repetição do ciclo. Em PASCAL esta estrutura pode
assumir duas formas:
FOR indice := valor_inicial TO valor_final DO
ou
FOR indice := valor_inicial DOWNTO valor_final DO
No primeiro caso a variável de controlo de ciclo ‘indice’ será incrementada de uma unidade
por cada vez que o ciclo se repete (o valor de y é sempre 1). Neste caso o valor valor_inicial é
80
menor que o valor valor_final. No segundo caso a variável de controlo de ciclo ‘indice’ será
decrementada de uma unidade por cada vez que o ciclo se repete (o valor de y é sempre –1).
Neste caso o valor valor_inicial é maior que o valor valor_final.
Pode-se desenhar um algoritmo para o cálculo do factorial de um número N baseado
nesta estrutura. A parte principal do algoritmo será
fact ß 1;
REPETIR PARA i ß 1 ATÉ N
fact ß i*fact
FIM REPETIR
e em PASCAL
fact:=1;
for i:=1 to N do
fact:=i*fact;
O algoritmo seguinte apresenta a tabuada (de 1 a 10) de um número introduzido pelo
teclado. Neste algoritmo a variável ‘numero’ guarda o valor introduzido pelo teclado, isto é, o
número do qual se pretende determinar a tabuada. A variável ‘i’ é usada como variável de
controlo de ciclo na estrutura REPETIR-PARA.
ALGORITMO Tabuada
VARIÁVEIS numero, i DO TIPO INTEIRO
1. [Ler numero da tabuada]
ESCREVER('Qual o número para o qual quer calcular a tabuada? ')
LER(numero)
2. [Calcular a tabuada de 1 a 10 para o numero lido]
ESCREVER('Tabuada do ', numero)
REPETIR PARA i ß 1 ATÉ 10
ESCREVER(numero,' * ',i,' = ', numero * i)
FIM REPETIR
81
3. FIM DO ALGORITMO
A sua codificação em PASCAL é
program tabuada;
var numero, i : integer;
begin
{Ler numero da tabuada}
write('Qual o número para o qual quer calcular a tabuada? ');
readln(numero);
{Calcular a tabuada de 1 a 10 para o numero lido}
writeln('Tabuada do ', numero);
for i:=1 to 10 do
writeln(numero,' * ',i,' = ', numero * i)
end.
Assim como era possível ter uma construção SE-ENTÃO-SENÃO dentro de outra
estrutura SE-ENTÃO-SENÃO, também é possível ter um ciclo no interior de outro ciclo. A
construção interna deve ser completamente “encaixada” na construção externa. O seguinte
algoritmo ilustra este conceito, determinando as tabuadas dos números de 1 a 10. Neste
algoritmo, a variável ‘i’ determina o número para o qual se está a calcular a tabuada.
ALGORITMO Tabela da tabuada
VARIÁVEIS i, j do tipo inteiro
1. [Cria 1ª linha da tabela]
REPETIR PARA j ß 1 ATÉ 10
ESCREVER(j, ‘
’)
FIM REPETIR
2. [Cria as seguintes 10 linhas da tabela]
REPETIR PARA i ß 1 ATÉ 10
ESCREVER(i, ‘
’)
82
REPETIR PARA j ß 1 ATÉ 10
ESCREVER(i*j , ‘
’)
FIM REPETIR
FIM REPETIR
3. FIM DO ALGORITMO
A codificação em PASCAL é
program tabela_da_tabuada;
uses crt;
var i, j : integer;
begin
clrscr;
for j:=1 to 10 do
write(j :4);
for i:=1 to 10 do
begin
writeln;
write(i :4);
for j:=1 to 10 do
begin
write(i*j :4)
end
end
end.
Neste momento convém esclarecer algumas situações particulares respeitantes à
codificação. A instrução ‘uses crt’ é utilizada para indicar ao compilador de PASCAL que
iremos utilizar funções que estão de algum modo relacionadas com o écran (Cathode Ray Tub
83
– CRT, veja-se a secção 4.4.5). Iremos utilizar a instrução ‘clrscr’ (limpar o écran). Note-se
ainda que a utilização da instrução ‘writeln’ sem qualquer argumento (sozinha) tem por
objectivo ‘saltar’ uma linha no écran. Nas instruções ‘write’ surge o argumento ‘:4’, por
exemplo ‘write(i*j :4)’. Isto indica ao compilador que deve usar no mínimo o espaço
correspondente a 4 caracteres para a parte inteira do número, quando escrever o resultado no
écran, mesmo que não seja necessário. Também é possível indicar um argumento deste tipo
referente à parte decimal do número, no caso de números do tipo real (por exemplo
‘write(x:4:2)’, correspondendo a utilizar 4 casas inteiras e 2 decimais). Estas técnicas são
usadas para dar um aspecto mais ordenado à forma como os dados são apresentados no écran
– neste exemplo a tabuada deve aparecer com o seu aspecto tradicional de tabela.
84
8 – Subalgoritmos – funções e procedimentos
Tal como já foi notado anteriormente, um problema complexo é mais fácil de resolver se
o dividirmos em pequenos sub-problemas. Os sub-algoritmos ou subprogramas destinam-se
principalmente a:
•
Permitir a criação de rotinas ou partes de código que podem ser usadas mais do
que uma vez no programa;
•
Estruturar melhor o programa;
•
Escrever determinadas porções de código de uma forma mais autónoma;
•
Construir programas de leitura, compreensão e correcção mais fáceis.
A utilização de subprogramas afecta o controlo de fluxo num algoritmo. Quando se dá a
chamada a um subprograma, a sequência de operações do programa passa para o subprograma
em questão até que este termine. Em seguida, a sequência de operações é retomada no ponto
logo a seguir onde essa chamada foi feita. A Figura 44 ilustra este processo.
Programa
Principal
•
•
•
Controlo transferido para a função sqrt
Função sqrt
Instruções
definindo a
função
Controlo devolvido ao programa principal
Val ← sqrt(x) + sqr(x)
•
•
•
Controlo devolvido ao programa principal
Função sqr
Instruções
definindo a
função
Controlo transferido para a função sqr
Figura 44 – Ilustração do processo de ‘passagem de controlo de fluxo ’.
8.1 – Parâmetros e argumentos
Em muitos casos, os subprogramas aceitam parâmetros – elementos que permitem passar
dados ou valores do programa ao subprograma e, eventualmente, também no sentido contrário
– o que lhes confere uma grande flexibilidade de utilização em situações diferenciadas.
85
Esta necessidade já foi observada quando vimos as funções pré-definidas. Como vimos,
estas funções já se encontram definidas e implementadas (e.g., sin, cos, abs, sqrt, sqr). São
utilizadas directamente em expressões como se fossem variáveis regulares. Por exemplo:
valor ß sin(angulo) + cos(angulo).
É da responsabilidade do programador fornecer o(s) argumento(s) (angulo, no exemplo
acima), sobre os quais a função opera para o cálculo do resultado de saída. No seguinte
excerto de algoritmo, as variáveis x e y são usadas como argumentos (de entrada) para a
função com nome ‘sqrt’ (que serve para calcular a raiz quadrada):
xß3
yß5
res1 ß sqrt(x) [determina a raiz quadrada de 3]
res2 ß sqrt(y) [determina a raiz quadrada de 5].
Neste momento convém fazer a distinção entre parâmetro e argumento. Os parâmetros
são identificadores inseridos no cabeçalho de definição dos subprogramas e são usados para
passar dados do programa principal para o subprograma e/ou vice versa. Podem ser usados
nas instruções operativas dos subprogramas. Tal como acontece com as variáveis, cada
parâmetro tem um tipo de dados associado a si. Geralmente, um subprograma recebe uma
lista de parâmetros, podendo esta lista ser nula.
Os argumentos são valores passados aos subprogramas em correspondência com os
parâmetros. Na altura em que é feita a chamada ao subprograma, é tida em consideração a
ordem dos argumentos, bem como o tipo de dados a que pertencem. Os argumentos utilizados
nas chamadas dos subprogramas podem ser valores directos, variáveis ou expressões, desde
que os valores sejam compatíveis no tipo de dado com os parâmetros correspondentes.
8.2 – Tipos de passagem de parâmetros
Existem dois métodos de associação entre argumentos e parâmetros:
•
Por cópia de valor (parâmetros de entrada);
•
Por referência (parâmetros de entrada/saída).
No primeiro caso, sempre que são feitas alterações nos parâmetros, dentro do subprograma,
essas alterações não resultam em mudanças nos argumentos correspondentes, isto porque os
86
parâmetros não estão ligados aos próprios argumentos (é feita uma cópia dos valores). No
segundo caso, as alterações efectuadas nos parâmetros, dentro do subprograma, reflectem-se
nos respectivos argumentos. É passada a própria variável (mais concretamente o seu endereço
de memória). Na codificação PASCAL utiliza-se a palavra-chave VAR para identificar este
tipo de parâmetros.
8.3 – Funções
Para além de utilizar as funções pré-definidas o programador pode definir as suas
próprias funções. Neste ponto veremos como se define uma função.
Em linguagem algorítmica, uma função define-se da seguinte forma:
FUNÇÃO nome (parâmetro1, parâmetro2, …, parâmetron)
TIPO DOS PARÂMETROS
VARIÁVEIS LOCAIS
Conjunto de instruções
RETORNAR(valor_de_saída)
O único valor que a função retorna é dado como uma expressão entre parêntesis a seguir à
palavra-chave RETORNAR. RETORNAR indica o ponto em que o controlo regressa ao
algoritmo principal, onde o valor devolvido pela função é então utilizado como parte da
expressão na qual a função se insere.
Vejamos um exemplo para melhor clarificar estes conceitos. Vamos supor que queremos
construir uma função para calcular g(x,y)=x2+y2.
ALGORITMO teste da função gdexy
FUNÇÃO gdexy(x, y)
PARÂMETROS DE ENTRADA x, y DO TIPO REAL
VARIÁVEIS LOCAIS res DO TIPO REAL
res ß sqr(x) + sqr(y)
RETORNAR (res)
87
VARIÁVEIS a, b, res DO TIPO REAL
aß5
bß3
res ß gdexy(a,b)
ESCREVER(res)
res ß gdexy(a * b+3 , 2)
ESCREVER(res)
FIM DO ALGORITMO
A função gdexy(x,y) possui dois parâmetros de entrada e para cada avaliação da função é
necessário o mesmo número de argumentos. É estabelecida uma correspondência entre os
parâmetros e os argumentos fornecidos à função. Quando fazemos res ß gdexy(a,b), a e b
são os argumentos. Ao parâmetro x corresponde o argumento a e ao parâmetro y corresponde
o argumento b. A ordem é crucial; gdexy(a,b) tem um resultado diferente de gdexy(b,a). A
codificação em PASCAL é:
program testa_func_gdexy;
function gdexy(x, y:real):real;
var res:real;
begin
res := sqr(x) + sqr(y);
gdexy := res
end;
var a, b, res:real;
begin
a := 5;
b := 3;
res := gdexy(a,b);
write(res);
88
res := gdexy(a * b+3 , 2);
write(res)
end.
Note-se que o valor calculado pela função é retornado afectando directamente o
identificador (nome) da função, ou seja, fazendo ‘gdexy := res’.
8.4 – Procedimentos
Embora o comportamento de uma função e de um procedimento seja similar no que diz
respeito à alteração do fluxo, da correspondência argumento/parâmetro e no conceito de
variáveis locais e variáveis globais, existem contudo duas diferenças fundamentais:
•
Um procedimento é chamado por meio do comando chamar;
•
Não existe retorno de um único valor como no caso da função. Qualquer valor a ser
retornado por um procedimento é devolvido através da lista de parâmetros. Podemos
retornar o número de parâmetros que desejarmos.
Os procedimentos definem-se da seguinte forma:
PROCEDIMENTO nome (parâmetro1, parâmetro2, …, parâmetron)
TIPO DOS PARÂMETROS DE ENTRADA
TIPO DOS PARÂMETROS DE ENTRADA/SAÍDA
VARIÁVEIS LOCAIS
Conjunto de instruções
RETORNAR
Ilustremos a utilização dos procedimentos. Pretende-se desenvolver um procedimento
que execute a divisão de dois valores inteiros e devolva como resultados o quociente e o resto
(da divisão inteira). Note-se que os parâmetros ‘dividendo’ e ‘divisor’ são passados por cópia
de valor, enquanto que os parâmetros ‘quociente’ e ‘resto’ são passados por referência
(porquê ?).
ALGORITMO testa procedimento divide
PROCEDIMENTO divide(dividendo, divisor, quociente, resto)
89
PARÂMETROS DE ENTRADA dividendo, divisor DO TIPO INTEIRO
PARÂMETROS DE ENTRADA/SAÍDA quociente, resto DO TIPO INTEIRO
quociente ß dividendo DIV divisor
resto ß dividendo MOD divisor
RETORNAR
VARIÁVEIS a, b, c, d DO TIPO INTEIRO
aß5
bß3
CHAMAR divide(a, b, c, d)
ESCREVER(c, d) [o que é que escreve no écran?]
CHAMAR divide(a*b , 2, c, d)
ESCREVER(c, d) [o que é que escreve no écran?]
FIM DE ALGORITMO
8.5 – Variáveis globais e variáveis locais
Quando uma variável é declarada na parte declarativa do programa principal, diz-se que é
uma variável global, ou que tem um alcance ou raio de acção global. Iisto quer dizer que é
reconhecida em todo o programa, não só na parte operativa do programa principal, mas
também dentro de qualquer subprograma.
Quando uma variável é declarada na parte declarativa de um subprograma então diz-se
que se trata de uma variável local ou que tem um raio de acção local. Isto implica que a
variável só é reconhecida dentro desse mesmo subprograma em que foi declarada.
Ilustremos estes conceitos recorrendo a um programa em PASCAL:
90
program testa_procedimento_divide(input,output);
uses crt;
procedure divisao(x,y:integer;var z,k:integer);
var b:integer; {variável local}
begin
b:=3;
a:=b+2;
z:=x div y;
k:=x mod y
parte operativa
Sub-programa
parte declarativa
{x, y passagem por copia de valor – z, k passagem por referência }
parte declarativa
var a:integer; {variável global}
end;
var dividendo,divisor,quociente,resto:integer; {variáveis locais}
clrscr;
parte operativa
dividendo:=8;
divisor:=3;
call divisao(dividendo,divisor,quociente,resto);{chamada do procedimento}
Programa principal
begin
writeln(quociente,resto);
repeat until keypressed {espera que uma tecla seja pressionada}
end.
Como se pode concluir do exemplo anterior, num algoritmo ou programa não se pode
dizer que existe uma parte declarativa e uma parte operativa, uma vez que nos subprogramas
também existem as duas partes. As variáveis ‘dividendo’, ‘divisor’, ‘quociente’ e ‘resto’ são
conhecidas apenas no programa principal (variáveis locais). A variável ‘b’ é conhecida apenas
no procedimento (variável local), enquanto que a variável ‘a’ é conhecida em todo o programa
(variável global).
91
8.6 – Subprogramas encaixados
Em certos casos, normalmente em programas com certa extensão e/ou complexidade, o
estilo de programação estruturada leva a incluir subprogramas dentro de outros subprogramas.
Tal como acontece com as variáveis locais, estes subprogramas só são “conhecidos” dentro
dos subprogramas onde são definidos.
O exemplo seguinte ilustra esta situação. Dada a margem de lucro e o custo de um artigo,
determinar o seu preço de venda ao público sabendo que a taxa de IVA a aplicar é de 19%.
ALGORITMO Etiquetagem
CONSTANTE IVA=19
VARIÁVEIS lucro, quantia DO TIPO REAL
FUNÇÃO pvp(custo)
PARÂMETROS DE ENTRADA custo DO TIPO REAL
RETORNA pvp DO TIPO REAL
VARIÁVEIS quantia DO TIPO REAL
FUNÇÃO pcIVA(preco)
PARÂMETROS DE ENTRADA preco DO TIPO REAL
RETORNA pcIVA DO TIPO REAL
VARIÁVEIS imposto DO TIPO REAL
imposto ß preco*IVA/100 [Cálculo do valor do IVA]
pcIVA ß preco+imposto [Calcular o P.V.P.]
RETORNA(pcIVA)
[Calcular o preço acrescido do lucro]
quantia ß custo*(100+lucro)/100
[Calcular o P.V.P.]
pvp ß pcIVA(quantia)
RETORNA(pvp)
[Ler lucro associado a cada produto]
92
ESCREVER('Percentagem de lucro (%): ')
LER(lucro)
[Ler custo dos artigos e escrever o seu p.v.p. até este ser nulo]
REPETIR
ESCREVER('Custo do artigo: ')
LER(quantia)
SE (quantia > 0) ENTÃO
ESCREVER('P.V.P. = ', pvp(quantia))
FIM DE SE
ATÉ quantia=0
FIM DO ALGORITMO
A codificação em PASCAL é:
program etiquetagem;
uses crt;
const IVA=19;
var lucro, quantia : real;
function pvp(custo: real): real;
var quantia: real;
function pcIVA(preco : real): real;
var imposto : real;
begin {Calcular o valor do IVA }
imposto:=preco*IVA/100; {Calcular o P.V.P.}
pcIVA:=preco+imposto;
end;
begin {Calcular o preco acrescido do lucro}
quantia:=custo*(100+lucro)/100;
93
{Calcular o P.V.P.}
pvp:=pcIVA(quantia)
end;
begin
{Ler lucro associado a cada produto}
write('Percentagem de lucro (%): ');
readln(lucro);
{Ler custo dos artigos e escrever o seu p.v.p. até ler custo nulo}
repeat
write('Custo do artigo: ');
readln(quantia);
if (quantia > 0) then
writeln('P.V.P. = ', pvp(quantia));
until quantia=0;
repeat until keypressed {espera que uma tecla seja pressionada}
end.
94
9 – Vectores e matrizes
Até ao momento estudámos dados simples ou não estruturados. Como já foi visto, os
dados simples ou não estruturados, dividem-se em:
•
Ordinais;
•
Inteiros;
•
Caracteres;
•
Lógicos;
•
Reais.
Vimos que as variáveis ou dados simples permitem guardar apenas um valor (de cada
vez). Como iremos ver, os dados estruturados permitem-nos uma maior flexibilidade de
programação. Os dados estruturados englobam:
•
Arrays (vectores e matrizes);
•
Registos ou records.
Iremos começar por estudar os dados estruturados do tipo array. Um array agrupa numa
mesma variável um conjunto de valores do mesmo tipo. Uma variável deste tipo é designada
por um identificador, mas cada elemento tem um ou mais índices associados.
Os arrays podem ser:
•
Unidimensionais, designando-se vulgarmente por vectores, os quais possuem um
único índice;
•
Multidimensionais, designando-se vulgarmente por matrizes, os quais possuem
um número de índices igual ao número de dimensões da matriz.
9.1 – Arrays unidimensionais – Vectores
É comum pensar-se num array do ponto de vista gráfico, associando-o à representação
feita pela álgebra linear de vector ou matriz, consoante o caso. Na Figura 45 pode ver-se a
interpretação gráfica associada ao vector.
95
A3 45
A4 13
A5 32
Vector coluna
A1 10
A2 12
10
A1
Vector linha
12 45 13 32
A2 A3 A4 A5
Figura 45 – Interpretação gráfica de vector.
A sintaxe algorítmica usada para definir um vector é a seguinte:
VARIÁVEIS
nome[número de elementos do vector] DO TIPO VECTOR DE tipo
Tal como anteriormente, nome designa o identificador pelo qual o vector será conhecido.
Depois do nome, e entre parêntesis rectos, devemos indicar o número de elementos do vector.
Finalmente devemos indicar que esta variável é do tipo vector e o tipo de dados de cada
elemento, isto é, cada elemento do vector é do tipo tipo. Vejamos alguns exemplos:
•
v[10] DO TIPO VECTOR DE REAIS, define um vector chamado v com 10
elementos do tipo real;
•
u[6] DO TIPO VECTOR DE CARACTERES, define um vector chamado u com
6 elementos do tipo caracter.
Em PASCAL as definições devem ser feitas da seguinte forma:
VAR nome : ARRAY[intervalos dos índices] OF tipo
Os exemplos anteriores definem-se como:
•
v: array[1..10] of real
•
u: array[1..6] of char.
Usando notação algorítmica, o acesso a um elemento particular de um vector é feito da
seguinte forma:
•
v[3] ß 4
•
u[2] ß ‘c’
•
a ß v[10]
96
•
ESCREVER(u[2])
•
Para i = 3, usando v[i] acedemos ao terceiro elemento do vector v.
Vejamos um exemplo concreto de aplicação de vectores. Suponhamos que a empresa
K3K pretende manipular os dados relativos às despesas em cada um dos 12 meses do ano. A
estrutura de dados deve ser definida da seguinte forma:
•
VARIÁVEIS
despesas[12]
DO
TIPO
VECTOR
DE
REAIS
(notação
algorítmica);
•
Var despesas: array[1..12] of real (PASCAL).
A introdução ou leitura dos valores é feita da seguinte forma:
(em notação algorítmica)
REPETIR PARA i ß 1 ATÉ 12
LER(despesas[i])
FIM REPETIR
(em PASCAL)
for i:=1 to 12 do
read(despesas[i]);
A apresentação no écran de todos os valores:
(em notação algorítmica)
REPETIR PARA i ß 1 ATÉ 12
ESCREVER(despesas[i])
FIM REPETIR
(em PASCAL)
for i:=1 to 12 do
writeln(despesas[i]);
O algoritmo seguinte, lê as despesas do ano e calcula a despesa média. Na segunda parte do
algoritmo, verifica-se para cada mês se as despesas foram superiores ou inferiores à média.
97
ALGORITMO despesa_media
VARIÁVEIS despesas[12] DO TIPO VECTOR DE REAIS
i DO TIPO INTEIRO
total_ano, media_ano DO TIPO REAL
total_ano ß 0
REPETIR PARA i ß 1 ATÉ 12
LER(despesas[i])
total_ano ß total_ano + despesas[i]
FIM REPETIR
media_ano ß total_ano/12
REPETIR PARA i ß 1 ATÉ 12
SE despesas[i] > media_ano ENTÃO
ESCREVER(’Despesas acima da média')
SENÃO
ESCREVER(’Despesas abaixo da média')
FIM SE
FIM REPETIR
FIM ALGORITMO
A função seguinte devolve as despesas para o mês i:
FUNÇÃO consulta(despesas, i)
PARÂMETROS DE ENTRADA:
despesas DO TIPO VECTOR DE REAIS
i DO TIPO INTEIRO
RETORNAR(despesas[i])
A seguinte função devolve o total das despesas para os n primeiros meses:
FUNÇÃO soma_n(despesas,n)
98
PARÂMETROS DE ENTRADA:
despesas DO TIPO VECTOR DE REAIS
n DO TIPO INTEIRO
VARIÁVEIS:
soma DO TIPO REAL
i DO TIPO INTEIRO
soma ß 0
REPETIR PARA i ß 1 ATÉ n
soma ß soma + despesas[i]
FIM REPETIR
RETORNAR(soma)
Este procedimento insere na posição i do vector despesas o valor x.
PROCEDIMENTO insere(despesas, i, x)
PARÂMETROS DE ENTRADA:
i DO TIPO INTEIRO
x DO TIPO REAL
PARÂMETROS DE ENTRADA SAÍDA:
despesas DO TIPO VECTOR DE REAIS
despesas[i] ß x
RETORNAR
O procedimento seguinte recebe o vector com as despesas e devolve o mês em que houve
maiores despesas e o respectivo valor.
PROCEDIMENTO maior_gasto(despesas, mes, valor)
PARÂMETRO DE ENTRADA:
despesas DO TIPO VECTOR DE REAIS
PARÂMETRO DE ENTRADA SAÍDA:
99
mês DO TIPO STRING
valor DO TIPO REAL
VARIÁVEIS:
i, max DO TIPO INTEIRO
max ß 1
valor ß despesas[1]
REPETIR PARA i ß 2 ATÉ 12
SE despesas[i] > valor ENTÃO
valor ß despesas[i]
max ß i
FIM SE
FIM REPETIR
CASO max POSSUA VALOR
1: mes ß 'Janeiro'
2: mes ß 'Fevereiro'
3: mes ß 'Março'
4: mes ß 'Abril'
5: mes ß 'Maio'
6: mes ß 'Junho'
7: mes ß 'Julho'
8: mes ß 'Agosto'
9: mes ß 'Setembro'
10: mes ß 'Outubro'
11: mes ß 'Novembro'
12: mes ß 'Dezembro'
FIM CASO
100
RETORNAR
Como exercício, sugere-se o desenvolvimento de um algoritmo ou programa que
implemente as funcionalidades implícitas no menu seguinte:
1 – Introduzir despesas (12 meses)
2 – Introduzir despesa para o mês i
3 – Consultar despesa total
4 – Consultar despesas do mês i
5 – Despesa méida
6 – Maior despesa
0 – Sair (terminar) do programa.
9.1.1 – Tratamento de strings ou cadeias de caracteres como arrays unidimensionais
Um dado do tipo string que tenha um determinado número de caracteres pode ser tratado
como um array unidimensional (vector) de elementos do tipo caracter ou char.
Por exemplo, o seguinte programa, codificado em PASCAL, lê uma palavra constituída
por seis caracteres e conta o número de vogais dessa palavra.
program vogal;
uses crt;
var
palavra : string[6];
i,conta:integer;
begin
clrscr;
writeln('Insira a palavra: ');
readln(palavra);
conta:=0;
for i:=1 to 6 do
if palavra[i] in ['a','e','i','o','u'] then
101
conta:=conta+1;
writeln(‘O número de vogais na palavra ’, palavra, ‘ é ’, conta);
repeat until keypressed
end.
Repare-se atentamente na expressão ‘palavra[i] in ['a','e','i','o','u']’. Esta expressão constitui a
parte central do programa, pois ela permite avaliar de uma forma semi-automática, quais os
caracteres que pertencem ao conjunto das vogais. Se não fosse possível utilizar esta
construção em PASCAL poderíamos usar algo semelhante:
If ((palavra[i]=‘a’) or (palavra[i]=‘e’) or (palavra[i]=‘i’) or
(palavra[i]=‘o’) or (palavra[i]=‘u’)) then
conta:=conta+1;
9.2 – Arrays bidimensionais ou matrizes
É possível definir um array de arrays, o que equivale a falar em arrays
multidimensionais. Se considerarmos um array bidimensional (matriz), podemos facilmente
representá-la como uma tabela ou quadro de duas entradas, em que se atribui um índice às
colunas e outro índice às linhas, à imagem do que é sugerido pela álgebra (veja-se a Figura
46).
A11
10
A12
13
linha 2
A21
22
A22
33
linha 3
A31
91
A32
23
linha 1
A linha, coluna ≡ A[linha, coluna]
coluna 1 coluna 2
Figura 46 – Interpretação geométrica de array bidimensional.
A sintaxe algorítmica para definição de uma matriz é a seguinte:
VARIÁVEIS nome[número de linhas, número de colunas] DO TIPO MATRIZ DE tipo
102
Note-se a existência de dois índices: o primeiro associado ao número da linha da matriz; e o
segundo ao número da coluna. Uma vez mais aconselhamos a observação da Figura 46.
Vejamos alguns exemplos de definições de matrizes:
•
v[3,4] DO TIPO MATRIZ DE REAIS, define uma matriz com três linhas e quatro
colunas, com elementos do tipo real (totalizando doze elementos);
•
u[6,6] DO TIPO MATRIZ DE CARACTERES, define uma matriz de seis linhas
e seis colunas, com elementos do tipo caracter (totalizando trinta e seis
elementos).
Em PASCAL as matrizes definem-se da seguinte forma:
VAR nome : ARRAY[número de linhas, número de colunas] OF tipo
Os exemplos anteriores definem-se da seguinte forma:
•
v : array[1..3,1..4] of real;
•
u : array[1..6,1..6] of char.
Numa estrutura deste tipo, os dados podem ser carregados utilizando ciclos encaixados. Em
notação algorítmica
REPETIR PARA i ß 1 ATÉ numero_de_linhas
REPETIR PARA j ß 1 ATÉ numero_de_colunas
LER(a[i, j])
FIM REPETIR
FIM REPETIR
A variável i é usada para índice das linhas e a variável j é usada para índice das colunas. Em
PASCAL isto é equivalente a termos
for i:=1 to numero_de_linhas do
for j:=1 to numero_de_colunas do
read(a[i,j]);
Desenvolvendo o exemplo da empresa K3K, suponhamos que além de guardar as
despesas dos meses também se desejava guardar as receitas obtidas em cada um dos doze
meses do ano. Neste caso a informação podia ser guardada numa matriz bidimensional, de
103
nome ‘despesas’, com as dimensões de 12 linhas (meses) por 2 colunas (despesas e receitas).
Alternativamente podia-se definir ‘despesas’ como possuindo 2 linhas (correspondentes às
receitas) e 12 colunas (correspondentes aos meses). A estrutura de dados é declarada da
seguinte forma (assume-se que a primeira coluna corresponde às despesas e a segunda às
receitas):
(linguagem algorítmica)
VARIÁVEIS despesas[12,2] DO TIPO MATRIZ DE REAIS
(em PASCAL)
VAR despesas : ARRAY[1..12,1..2] OF REAL
A atribuição de um valor é feita da seguinte maneira:
(linguagem algorítmica)
despesas[3,1] ß 5000
(em PASCAL)
despesas[3,1] := 5000
A apresentação no écran de todos os valores é feita da seguinte forma
(linguagem algorítmica)
REPETIR PARA i ß 1 ATÉ 12
REPETIR PARA j ß 1 ATÉ 2
ESCREVER(despesas[i,j])
FIM DE REPETIR
FIM DE REPETIR
(em PASCAL)
for i:=1 to 12 do
for j:=1 to 2 do
writln(despesas[i,j]);
Por seu lado, a leitura de todos os valores é feita utilizando
(linguagem algorítmica)
104
REPETIR PARA i ß 1 ATÉ 12
REPETIR PARA j ß 1 ATÉ 2
LER(despesas[i,j])
FIM DE REPETIR
FIM DE REPETIR
(em PASCAL)
for i:=1 to 12 do
for j:=1 to 2 do
read(despesas[i,j])
A função abaixo calcula as despesas ao longo de um ano
FUNÇÃO gasto(despesas)
PARÂMETROS DE ENTRADA:
despesas DO TIPO MATRIZ DE REAIS
VARIÁVEIS i DO TIPO INTEIRO
total DO TIPO REAL
total ß 0
REPETIR PARA i ß 1 ATÉ 12
total ß total +despesas[i,1]
RETORNAR(total)
9.3 – Arrays multidimensionais
Existem aplicações onde se pode utilizar estruturas tridimensionais, que são
representadas por conjuntos de três índices. Por exemplo, na análise de sequências de imagens
pode ser usada uma estrutura deste tipo, onde ao primeiro índice se associa o número da
imagem na sequência, ao segundo, o número da linha naquela imagem e ao terceiro, o número
da coluna naquela imagem.
105
Embora seja possível ter conjuntos com mais de três índices, é difícil ilustrá-los com
exemplos reais, sendo apenas utilizados em aplicações muito específicas.
106
10 – Pesquisa e ordenação com vectores
Pesquisar um vector consiste basicamente em percorrer os seus elementos a fim de
localizar um item desejado.
Ordenar ou classificar um vector é a operação de arranjar os seus elementos numa ordem
sequencial, utilizando um critério de seriação.
Existem vários métodos de pesquisa e ordenação. Dadas as características introdutórias
que revestem esta disciplina, iremos abordar apenas dois métodos de ordenação: ordenação
por selecção (selection sort); e ordenação por borbulhamento (bobble sort).
10.1 – Ordenação ou classificação
Tal como já foi dito, vamos estudar a classificação pelos métodos de:
•
Selecção (selection sort);
•
Borbulhamento (bobble sort).
Nestes métodos assume-se que lidamos com vectores de N elementos e pretendemos
ordená-los por ordem ascendente. Os princípios usados serão os mesmos se pretendermos
ordenar o vector por ordem descendente.
10.1.1 – Ordenação por selecção
O algoritmo de ordenação/classificação por selecção consiste nos seguintes passos:
1. Partindo do primeiro elemento do vector faz-se uma pesquisa para localizar o
elemento com menor valor;
2. Este elemento é permutado com o primeiro elemento do vector (esta troca coloca o
elemento com o menor valor na primeira posição do vector);
3. Executa-se uma pesquisa para o segundo menor valor; isto é feito pelo exame dos
valores de cada elemento a partir da segunda posição;
4. O elemento encontrado é permutado com o situado na segunda posição do vector;
5. Repetem-se os passos de pesquisa do n-ésimo menor elemento (correspondente ao
ponto 3.) e permuta deste elemento com o elemento na n-ésima posição no vector
107
(correspondente ao ponto 4.) até que todos os elementos tenham sido colocados
por ordem ascendente.
Se o vector possui N elementos são necessárias N – 1 passagens para o ordenar (a última
passagem não é necessária).
O seguinte algoritmo ordena um vector V de N elementos por ordem ascendente
utilizando este método.
VARIÁVEIS indice_min, passagem, l DO TIPO INTEIRO
1. [Executa N-1 passagens]
REPETIR PARA passagem ß 1 ATÉ N-1
2. [Inicializa o índice do mínimo]
indice_min ß passagem
3.[Executa uma passagem e obtém o índice do elemento com menor valor]
REPETIR PARA l ß passagem+1 ATÉ N
SE V[l] < V[indice_min] ENTÃO
indice_min ß l
FIM SE
FIM REPETIR
SE indice_min ≠ passagem ENTÃO
troca(V[passagem],V[indice_min])
FIM SE
FIM REPETIR
FIM ALGORITMO
A variável ‘passagem’ indica ou guarda o número da passagem actual, isto é, quantos
elementos do vector já se encontram ordenados. A variável ‘indice_min’ guarda a posição no
vector onde se encontra o elemento mais pequeno. A variável ‘l’ é usada para percorrer o
vector desde a posição indicada por ‘passagem’ até ao penúltimo elemento do vector (no final
de N – 1 passagens, o último elemento do vector será por certo o maior). Note-se ainda que só
108
é necessário efectuar a troca dos elementos do vector se por acaso o elemento actual não for o
mais pequeno.
10.1.2 – Ordenação por borbulhamento
Ao contrário do que acontece com o método de classificação por selecção, em que
primeiro se encontra o elemento com menor valor e só depois se efectua a troca, neste caso
dois elementos são trocados imediatamente após a descoberta de que eles estão fora de ordem.
São requeridas, no máximo, N – 1 passagens. O funcionamento do método é o seguinte:
1. Durante a primeira passagem, V[1] e V[2] são comparados e se estão fora de
ordem, são permutados;
2. Isto é repetido para os pares de elementos (V[2];V[3]), (V[3];V[4]), (...),
(V[i];V[i+1]). O método leva os elementos com maior valor a “borbulharem” para
o fim do vector. Após a primeira passagem, o elemento com maior valor ocupará
a posição N.
3. Em cada passagem sucessiva, os elementos com os valores imediatamente mais
baixos serão colocados nas posições N – 1, N – 2, N–3, ..., 3, 2, 1.
4. É feita uma verificação para ver se houve alguma troca de valores durante cada
passagem. Se não houve trocas durante a passagem, o vector encontra-se
classificado, não sendo necessárias mais passagens.
O algoritmo seguinte implementa este método.
VARIÁVEIS flag DO TIPO LÓGICO
i,j DO TIPO INTEIRO
iß1
flag ß verdade
REPETIR ENQUANTO (i ≤ N-1) E (flag=verdade)
flag ß falso
REPETIR PARA j ß 1 ATÉ N – i
SE V[j+1]<V[j] ENTÃO
troca(V[i],V[j])
109
flag ß verdade
FIM SE
FIM REPETIR
i ß i+1
FIM REPETIR
FIM ALGORITMO
A variável ‘flag’ é usada como marca para detectar se houve trocas ou não (daí o colocar-se
esta marca a verdadeiro quando existem trocas). Por outro lado, não havendo trocas o vector
encontra-se ordenado, permitindo a sua finalização. A condição de saída do ciclo REPETIRENQUANTO pode ser relaxada (simplificada), passando de ‘(i ≤ N-1) E (flag=verdade)’
simplesmente para ‘flag=verdade’ (porquê?). Alternativamente pode usar-se o seguinte
algoritmo, deixando a sua explicação como exercício:
VARIÁVEIS flag DO TIPO LÓGICO
i,j DO TIPO INTEIRO
iß1
REPETIR
flag ß falso
REPETIR PARA j ß 1 ATÉ N – i
SE V[j+1]<V[j] ENTÃO
troca(V[i],V[j])
flag ß verdade
FIM SE
FIM REPETIR
i ß i+1
ATÉ flag=falso
FIM ALGORITMO
110
Antes de passarmos ao ponto seguinte convém deixar ficar uma nota acerca da função
‘troca(a,b)’ utilizada nestes três últimos algoritmos. Esta função, tal como o seu nome sugere,
tem por objectivo trocar os conteúdos das variáveis a e b. Para isso torna-se necessária a
utilização de uma variável intermédia auxiliar, que guarde temporariamente um dos valores.
O código principal terá o seginte aspecto:
aux ß a
aßb
b ß aux
Nos exemplos vistos até ao momento, o vector ‘V’ possui dados não estruturados ou simples.
Isto que dizer que em cada elemento do vector apenas é guardado um tipo de dado. Como
iremos ver mais adiante (secção 0), se o tipo de dados for estruturado, num único elemento do
vector são guardados vários tipos de dados. Nesta situação, deve-se ‘trocar’ não só os dados
pelos quais estamos a efectuar a ordenação, mas também todos os outros que estão associados
aos elementos em causa do vector. Quando a linguagem de implementação é o PASCAL esta
observação tende a passar despercebida. Contudo, em linguagens como o C pode ser a causa
de várias horas de pesquisa ‘à procura do erro!’.
10.2 – Pesquisa
Embora existam várias técnicas amplamente usadas para pesquisar um elemento num
vector (verificar a existência ou não de um dado elemento), pelos motivos anteriormente
apresentados, estudaremos apenas duas técnicas:
•
Pesquisa linear;
•
Pesquisa binária.
10.2.1 – Pesquisa linear
A pesquisa linear envolve a verificação sequencial de cada elemento do vector até que o
elemento desejado seja encontrado.
Dado um vector V, ordenado ou não, com N elementos (N ≥ 1) o algoritmo seguinte
procura no vector um elemento particular cujo valor é x.
VARIÁVEIS
flag DO TIPO LÓGICO
111
i DO TIPO INTEIRO
iß1
flag ß falso
REPETIR ENQUANTO (i ≤ N) E (flag=falso)
SE V[i]=x ENTÃO
flag ß verdade
FIM SE
i ß i+1
FIM REPETIR
SE flag = verdade ENTÃO
ESCREVER('Valor encontrado na posição', i-1)
SENÃO
ESCREVER('Valor não encontrado')
FIM SE
FIM ALGORITMO
Caso o elemento seja encontrado a marca ‘flag’ é colocada a verdadeiro, permanecendo a
falso caso o elemento não exista no vector. Note-se que com uma ligeira modificação do
algoritmo, a pesquisa pode ser iniciada do último para o primeiro elemento do vector.
10.2.2 – Pesquisa binária
Vamos assumir que os N elementos do vector estão armazenados por ordem ascendente.
Um raciocínio idêntico é aplicável se o vector estiver ordenado por ordem descendente. O
algoritmo é o seguinte:
1. O meio do vector pode ser calculado e o seu valor é examinado.
2. Se o valor for mais alto, então o elemento do meio da primeira metade é
examinado, passando esta metade do intervalo a ser considerada como vector de
partida;
112
3. Se o valor for mais baixo então o elemento do meio da segunda metade é tentado,
passando esta metade do intervalo a ser considerada como vector de partida;
4. Repete-se desde 1. até que o valor desejado seja encontrado ou o intervalo de
pesquisa seja vazio.
Dado um vector V de N elementos ordenado por ordem crescente, o algoritmo seguinte
implementa o processo anterior para a pesquisa de um dado elemento x. As variáveis ‘alto’ e
‘baixo’ servem para indicar o intervalo de pesquisa (o primeiro e o último elemento do
intervalo). A variável ‘medio’ é usada para guardar a posição média do intervalo (o elemento
do meio do intervalo baixo-alto). A variável ‘flag’, quando no estado verdadeiro, assinala o
facto de se ter encontrado o elemento pretendido.
VARIÁVEIS
flag DO TIPO LÓGICO
alto, medio, baixo DO TIPO INTEIRO
baixo ß 1
alto ß N
flag ß falso
REPETIR ENQUANTO (baixo < alto) E (flag=falso)
medio ß (alto-baixo)/2
SE x < V[medio] ENTÃO
alto ß medio -1
SENÃO
SE x > V[medio] ENTÃO
Baixoß medio+1
SENÃO
flag ß verdade
FIM SE
FIM SE
FIM REPETIR
113
SE (baixo=alto) E (x=v[baixo]) ENTÃO
flag = verdade
medio = baixo
FIM SE
SE flag=verdade ENTÃO
ESCREVER('pesquisa bem sucedida na posição', medio)
SENÃO
ESCREVER('pesquisa mal sucedida.')
FIM SE
FIM ALGORITMO
Repare-se na existência da estrutura ‘SE (baixo=alto) E (x=v[baixo]) ENTÃO’. Esta
declaração é usada para indicar esta posição como solução no caso limite dos extremos do
intervalo se tocarem e este valor corresponder ao valor procurado. Repare-se que pode
relaxar-se a condição desta estrutura simplesmente para ‘SE x=v[baixo] ENTÃO’, uma vez
que o extremo esquerdo do intervalo pode corresponder ao local onde se encontra o valor
pretendido. Este caso só é verificado quando o extremo direito coincide com o esquerdo.
Neste caso o ponto médio do intervalo é baixo-alto=0 (daí a necessidade de ser tratado de
forma independente, i.e., fora do ciclo ‘REPETIR-ENQUANTO’.
114
11 – Registos e arrays de registos
Até agora discutimos a utilização de estruturas de dados simples ou elementares como:
•
caracter;
•
inteiro;
•
real;
•
lógico;
•
string.
Vimos também a utilização de arrays de elementos deste tipo. A característica
fundamental de um array reside no facto dos seus elementos serem todos do mesmo tipo.
Já vimos ao longo dos exemplos que foram apresentados, que um dos passos mais
importantes na resolução de um problema é a forma de estruturar convenientemente os dados
envolvidos. Por exemplo, no armazenamento de dados referentes a um estudante devemos ser
capazes de guardar:
•
O nome, sendo este do tipo string;
•
O número, sendo este do tipo inteiro;
•
O endereço, sendo este do tipo string;
•
O ano de matrícula, sendo este do tipo inteiro;
•
Etcetera.
Até ao momento não estudámos nenhum tipo de dado que seja capaz de armazenar desde
strings, até números inteiros, reais, etcetera. Necessitamos pois de uma estrutura nova capaz
de albergar dados que não sejam todos do mesmo tipo, isto é, dados não homogéneos. As
estruturas de dados do tipo REGISTO ou ESTRUTURA servem este propósito.
Vejamos um exemplo concreto. Suponhamos que pretendemos elaborar um conjunto de
programas que sejam capazes de lidar com números complexos. Como sabemos, os números
deste tipo possuem uma parte real e uma parte imaginária. Poderíamos pensar em utilizar duas
variáveis do tipo real, designando uma de ‘parte_real’ e outra de ‘parte_imaginaria’, por
exemplo. Seguindo esta linha de raciocínio, se por ventura pretendêssemos usar vectores (ou
115
matrizes) de números complexos, teríamos que definir um vector (ou uma matriz) de nome
‘parte_real’ e um outro (ou uma outra) de nome ‘parte_complexa’. A utilização de um registo
que albergue duas variáveis (campos) do tipo real, uma com nome ‘parte_real’ e outra com o
nome ‘parte_imaginaria’, facilitará grandemente a programação e a estruturação dos dados do
programa. As variáveis do registo são designadas de campos. Vejamos pois como é que
poderíamos definir um registo deste tipo, utilizando notação algorítmica:
complexo DO TIPO REGISTO
CAMPOS DO REGISTO
ParteReal DO TIPO REAL
ParteImaginaria DO TIPO REAL
FIM DE REGISTO
Em PASCAL define-se da seguinte forma:
TYPE
complexo = RECORD
ParteReal : REAL;
ParteImaginaria : REAL
END;
O acesso aos diferentes campos de um REGISTO faz-se da seguinte forma (tanto em
linguagem algorítmica como em PASCAL): nome_da_variavel.nome_do_campo. Se
definirmos A como sendo do tipo Complexo, podemos fazer:
(notação algorítmica)
VARIÁVEIS A DO TIPO complexo
A.ParteReal ß 2.3
ESCREVER(A.ParteImaginaria)
(em PASCAL)
VAR A : complexo;
A.ParteReal := 2.3
116
WRITE(A.ParteImaginaria)
Claro está que podemos definir funções/procedimentos que utilizem REGISTOS
previamente definidos. O procedimento que se segue soma dois números complexos.
(notação algorítmica)
PROCEDIMENTO AdicaoComplexa(A, B, C)
PARÂMETROS DE ENTRADA
A, B DO TIPO complexo [valores a adicionar]
PARÂMETROS DE ENTRADA E SAÍDA
C DO TIPO complexo [resultado da adição]
[Cálculo do resultado]
C.ParteReal ß A.ParteReal+B.ParteReal
C.ParteImaginaria ß A.ParteImaginaria+B.ParteImaginaria
RETORNA
(em PASCAL)
PROCEDURE AdicaoComplexa(
A, B : complexo; {valores a adicionar}
VAR C : complexo {resultado da adição}
);
BEGIN
{Cálculo do resultado}
C.ParteReal := A.ParteReal+B.ParteReal;
C.ParteImaginaria := A.ParteImaginaria+B.ParteImaginaria
END;
11.1 – Arrays de registos
Os arrays de registos definem-se da seguinte forma:
117
•
nome_do_vector[N] DO TIPO VECTOR DE nome_do_registo
•
nome_da_matriz[N, M] DO TIPO MATRIZ DE nome_do_registo
Como exemplo vejamos a definição e utilização de um vector com vinte elementos do
tipo complexo:
(notação algorítmica)
x[20] DO TIPO VECTOR DO TIPO complexo [Declaração]
x[2].ParteReal ß -3.56 [Acesso a um elemento]
Escrever(x[1].ParteImaginaria) [Acesso a um elemento]
(em PASCAL)
x : ARRAY[1..20] OF complexo; {Declaração}
x[2].ParteReal := -3.56; {Acesso a um elemento}
WRITE(x[1].ParteImaginaria); {Acesso a um elemento}
Finalizemos este estudo com o exemplo da estruturação e preenchimento de dados
referentes a um empregado da empresa K3K.
(notação algorítmica)
RegistoNome DO TIPO REGISTO
CAMPOS DO REGISTO
proprio DO TIPO string
apelido DO TIPO string
FIM DO REGISTO
RegistoMorada DO TIPO REGISTO
CAMPOS DO REGISTO
rua DO TIPO string
cidade DO TIPO string
provincia DO TIPO string
FIM DO REGISTO
118
RegistoEmpregado DO TIPO REGISTO
CAMPOS DO REGISTO
nome DO TIPO RegistoNome
morada DO TIPO RegistoMorada
salario DO TIPO real
sexo DO TIPO caracter
FIM DO REGISTO
VARIÁVEIS empregado[20] DO TIPO VECTOR DE RegistoEmpregado;
Para introduzir o nome próprio, via teclado, do i-ésimo empregado faz-se
LER(emrpegado[i].nome.proprio);
Para apresentar no écran alguns dados correspondentes ao i-ésimo empregado faz-se
ESCREVER(‘O empregado ’, empregado[i].nome.apelido, ‘ tem um salário de ’,
empregado[i].salario, ‘ Euro.’);
(em PASCAL)
type
RegistoNome = record
proprio : string[30] ;
apelido : string[30]
end;
RegistoMorada = record
rua : string[30];
cidade : string[30];
provincia : string[30]
end;
RegistoEmpregado = record
nome : RegistoNome;
119
morada : RegistoMorada;
salario : real;
sexo : char
end;
var empregado : array[1..20] of RegistoEmpregado;
Para introduzir o nome próprio, via teclado, do i-ésimo empregado faz-se
read(emrpegado[i].nome.proprio);
Para apresentar no écran alguns dados correspondentes ao i-ésimo empregado faz-se
writeln(‘O empregado ’, empregado[i].nome.apelido, ‘ tem um salário de ’,
empregado[i].salario:6:2, ‘ Euro.’);
Deixa-se como exercício final o desenvolvimento de um algoritmo que produza uma
listagem ordenada por ordem alfabética dos nomes próprios dos empregados. Desta listagem
deve ainda constar, para além do nome próprio, o apelido e o salário. Também deve ser
tentada a incorporação das funcionalidades pretendidas com o exercício acerca da empresa
K3K, secção 9.1.
120
12 – Bibliografia
Com esta secção não pretendemos de forma alguma indicar uma lista de referências
bibliográficas completa e exaustiva. Pretende-se sim indicar alguns textos que sirvam de
complemento ou pormenorização aos assuntos aqui introduzidos. Indicam-se referências para
os diferentes conteúdos aqui introduzidos, nomeadamente, evolução dos computadores,
sistemas de numeração, sistemas informáticos e respectivas classificações, noções de sistemas
operativos, noção de algoritmo e respectiva evolução dos algoritmos e da algoritmia, noções
de programação de computadores com recurso a diferentes linguagens de programação e
notações algorítmicas, etcetera.
Esperamos que esta lista seja de alguma forma útil e sirva de “ponto de entrada no mundo
dos computadores”.
Azul, Artur Auguto; Introdução às tecnologias de informação; Bloco I; Porto Editora; 2000.
Chabert, Jean-Luc (ed.); A History of algorithms: from the pebble to the microchip; SpringerVerlag; 1999.
Harel, David; Algorithmics: the spirit of computing; Addison-Wesley Publishing Company;
1987.
Monteiro, Mário A.; Introdução à organização de computadores; 2ª edição; Livros Técnicos
e Científicos (LTC) – Editora S.A. – Rio de Janeiro; 1995.
Peatman, John B.; Microcomputer-based design; International Student Edition; McGraw-Hill
International Book Company; 1981.
Tremblay, Jean-Paul; e DeDourek, John M.; e Bunt, Richard B.; Introduction to computer
science: an algorithmic approach; Pascal Edition; McGraw-Hill International Editions; 1989.
Didacta – Enciclopédia temática ilustrada; ISBN 972-8332-00-9; Madrid, Espanha; Edición
2000.
121
Download

Sebenta