Você está na página 1de 181

RICARDO SAID

CURSO DE LÓGICA DE
PROGRAMAÇÃO
© 2007 by Digerati Books
Todos os direitos reservados e protegidos pela Lei 9.610 de
19/02/1998.
Nenhuma parte deste livro, sem autorização prévia por escrito da
editora, poderá ser reproduzida ou transmitida sejam quais forem os
meios empregados: eletrônicos, mecânicos, fotográficos, gravação
ou quaisquer outros.

Diretor Editorial
Luis Matos

Assistência Editorial
Monalisa Neves
Erika Sá

Edição de Arte e
Projeto Gráfico
Daniele Fátima

Preparação dos originais


Fernanda Batista dos Santos

Revisão
Cárita Ferrari Negromonte

Diagramação
Rogério Chagas

Capa
Daniel Brito

Dados Internacionais de Catalogação na Publicação (CIP)


(Câmara Brasileira do Livro, SP, Brasil)
S132c Said, Ricardo.

Curso de Lógica de Programação /


Ricardo
Said. – São Paulo: Digerati Books, 2007.
144 p.

ISBN 978-85-60480-24-1

1. Programação (Computadores).
2. Algoritmos. I. Título.

CDD 005.1

Universo dos Livros Editora Ltda.


Rua Tito, 1.609
CEP 05051-001 • São Paulo/SP
Telefone: (11) 3648-9090 • Fax: (11) 3648-9083
www.universodoslivros.com.br
e-mail: editor@universodoslivros.com.br

Conselho Administrativo: Alessandro Gerardi, Alessio Fon


Melozo, Luis Afonso G. Neira, Luis Matos e William Nakamura.
Prefácio

A contínua expansão do alcance da informática faz que o


computador se torne cada vez mais comum, atingindo um número
maior de pessoas. Nessa diversidade de aplicações, destaca-se
como relevante a figura do programador, por seu papel ativo nesse
processo de popularização. Como áreas fundamentais para a
formação do profissional de programação, temos o desenvolvimento
lógico e a construção de algoritmos, assuntos deste livro.
Durante o estudo, deparamos freqüentemente com peculiaridades
relativas à abstração, estas se encontram relacionadas
sobremaneira com o desenvolvimento e aplicação da lógica
(matéria-prima para a elaboração de algoritmos), um fator que pode
suscitar uma lacuna no universo com o qual já estamos
familiarizados.
O objetivo primeiro deste livro é possibilitar compreensão e
naturalidade à lógica de programação, não se preocupando com a
codificação dos algoritmos desenvolvidos, mas, sim, com um
alicerce sólido que possibilite a migração para qualquer linguagem
de programação estruturada tradicional ou programação visual
(orientada a eventos).
Por meio de uma linguagem informal, busquei uma correlação dos
tópicos de cada capítulo, bem como exemplos tangíveis à realidade
e ao cotidiano, caráter didático que confere acessibilidade ao texto.
Essa característica permanece, inclusive, nos itens mais complexos,
sem demérito, no entanto, da profundidade e da importância.
Em vista dessas características e do conteúdo abrangido, o
presente material é ideal tanto como livro-texto, para cursos de
programação de computadores, quanto como fonte de estudo
independente, para aprimoramento técnico e para profissionais, ou
interessados, em lógica de programação.
Capítulo 1

Lógica e algoritmos

A chave do sucesso na vida é o conhecimento do valor das


coisas.

John Boyle O’Reilly


Introdução
Noções de lógica

O uso corriqueiro da palavra lógica está, normalmente,


relacionado à coerência e à racionalidade. Freqüentemente,
associa-se lógica apenas à matemática, não percebendo sua
aplicabilidade e relação com as demais ciências.
Podemos relacionar a lógica com a “correção do pensamento”,
pois uma de suas preocupações é determinar quais operações são
válidas e quais não são, fazendo análises das formas e leis do
pensamento. Como a filosofia, ela procura saber por que pensamos
assim, e não de outro jeito. Como arte ou técnica, ela nos ensina a
usar corretamente as leis do pensamento.
Poderíamos dizer, também, que a lógica é a “arte do bem pensar”,
que é a “ciência das formas do pensamento”. Visto que a forma
mais complexa do pensamento é o raciocínio, a lógica estuda a
“correção do raciocínio”. Podemos, ainda, dizer que a lógica tem em
vista a “ordem da razão”. Isso dá a entender que a nossa razão
pode funcionar desordenadamente. Por isso, a lógica estuda e
ensina a colocar “ordem no pensamento”.

Existe lógica no dia-a-dia ?

Sempre que pensamos, a lógica, ou a falta dela, nos acompanha.


Quando falamos ou escrevemos, estamos expressando nosso
pensamento, logo, precisamos usar lógica nessas atividades.
Podemos perceber a importância da lógica em nossa vida não só na
teoria, mas também na prática, já que, quando queremos pensar,
falar, escrever ou agir corretamente, precisamos colocar “ordem no
pensamento”, isto é, utilizar lógica.
Vejamos alguns exemplos:
A gaveta está fechada. A caneta está dentro da gaveta.
Precisamos, primeiro, abrir a gaveta para, depois, pegar a
caneta;
Anacleto é mais velho que Felisberto. Felisberto é mais velho
que Marivaldo. Portanto, Anacleto é mais velho que Marivaldo.
E a lógica de programação?
A lógica de programação significa o uso correto das leis do
pensamento nos processos de raciocínio e simbolização formais da
programação de computadores, objetivando racionalidade e
desenvolvimento de técnicas que cooperem para a produção de
soluções logicamente válidas e coerentes, que resolvam, com
qualidade, os problemas que enfrentamos.
O raciocínio é algo abstrato, intangível. Os seres humanos têm a
capacidade de expressá-lo por meio da palavra falada ou escrita,
que, por sua vez, se baseia em determinado idioma, que segue um
conjunto de padrões (gramática). Um mesmo raciocínio pode ser
expresso em qualquer um dos inúmeros idiomas existentes, mas
continuará representando o mesmo raciocínio, usando apenas outra
convenção.
Algo similar ocorre com a lógica de programação, que pode ser
concebida pela mente, testada no papel e, logo após, representada
em qualquer uma das inúmeras linguagens de programação
existentes. Estas, por sua vez, são muito atreladas a uma grande
diversidade de detalhes computacionais, que pouco têm que ver
com o raciocínio original. Para escapar dessa torre de Babel e, ao
mesmo tempo, representar mais fielmente o raciocínio da lógica de
programação, utilizamos os algoritmos.
O que é um algoritmo?
O objetivo do estudo da lógica de programação é a construção de
algoritmos coerentes e válidos. Porém, o que é um algoritmo?
Um algoritmo pode ser definido como uma seqüência finita de
passos, descritos em uma ordem lógica, que visam a atingir um
objetivo bem definido.
Conforme precisemos especificar uma seqüência de passos, é
necessário utilizar ordem, ou seja, “pensar com ordem”, utilizar
lógica.
Apesar do nome pouco usual, algoritmos são comuns em nosso
cotidiano, como, por exemplo, uma receita de bolo, na qual estão
descritos os diversos ingredientes necessários, além de uma
seqüência de vários passos (ações) que devem ser fielmente
cumpridos para que se consiga fazer o alimento desejado, como se
esperava, antes do início das atividades (objetivo bem definido).
Quando elaboramos um algoritmo, devemos especificar ações
claras e precisas, que, a partir de um estado inicial, após um
período de tempo finito, produzem um estado final previsível e bem
definido. Isso significa que o algoritmo fixa um padrão de
comportamento a ser seguido, uma norma de execução a ser
trilhada, com vistas a alcançar, como resultado final, a solução de
um problema, garantindo que, sempre que executado sob as
mesmas condições, produza o mesmo resultado.
O que é uma linguagem de
programação?
Linguagens de programação são conjuntos de padrões e
comandos com sintaxes predefinidas que o programador utiliza para
“traduzir” os algoritmos escritos, o que permite “dar ordens” ao
processador e à memória de um computador. Um algoritmo pode
ser escrito em qualquer linguagem de programação, mas a lógica é
a mesma. De acordo com os recursos oferecidos pela linguagem
escolhida, o resultado final pode ter uma interface simples em modo
texto, uma interface gráfica mais trabalhada, aparecer em uma
página da Web e assim por diante.
Assim como nas línguas faladas, as linguagens de programação
possuem diferenças de sintaxe e gramática, existem linguagens
mais simples ou mais complicadas e linguagens mais adequadas de
acordo com o tipo de tarefa a realizar. Algumas das mais
conhecidas linguagens de programação são: Assembly, Fortran,
Pascal, Clipper, Cobol, C, Visual C ++, Delphi, Visual Basic, Java,
ASP etc.
Algoritmos e resolução de
problemas
Mais do que ter conhecimento sobre linguagens e detalhes sobre
este ou aquele equipamento, a programação é uma atividade
diretamente ligada à resolução de problemas. Numa definição
simples, podemos considerar um problema como qualquer tipo de
questão levantada para chegar a uma decisão ou solução.
Quando elaboramos um programa de computador, queremos
ensinar a máquina a resolver um problema, e a eletrônica permite
que a máquina chegue a uma resposta de maneira mais rápida e
mais precisa do que nós. No entanto, como um programador poderá
escrever um programa se ele mesmo não conhece a solução?
Assim, a resolução de problemas é uma etapa que antecede a
programação em si.
Observe que a definição de “problema” é bastante genérica, o que
nos permite imaginar algoritmos para as mais diversas situações,
não necessariamente de caráter algébrico. Na realidade, nossa
própria memória já é recheada de algoritmos para a execução de
tarefas diariamente. Acompanhe os exemplos:
Você deseja retirar dinheiro de sua conta-corrente em um caixa
eletrônico. Você chega ao caixa, passa o cartão, digita sua
senha e escolhe a opção para a retirada. Em seguida, digita o
valor a sacar desejado, confirma o número e aguarda a saída
das notas. Nessa situação, você tinha um problema (sacar
dinheiro) e o resolveu, pois sabia qual era o algoritmo, isto é, a
seqüência de etapas para solucioná-lo (ir ao caixa, passar o
cartão, digitar a senha etc.);
Você entra em casa depois de um dia duro de trabalho. Ao
chegar em frente a seu prédio, destranca a porta do hall, entra
e tranca a porta novamente. Chama o elevador e aperta o
número de seu andar. Chegando a seu corredor, procura a
porta de seu apartamento; destranca a porta, entra e a tranca
novamente. Aqui, podemos dizer que você seguiu um algoritmo
para resolver o problema “entrar em casa”;
Você deseja beber um copo de água. Despeja água em um
copo até a borda e, em seguida, ergue o copo até os lábios e
toma os goles de água até o copo ficar vazio.

Embora pareça natural, seguimos um algoritmo para beber água,


tanto que, desde crianças, fomos “programados” a executá-lo e,
depois, fazemos as coisas inconscientemente.
Há uma observação importante neste ponto. Quando construímos
um algoritmo, fixamos um padrão de comportamento a ser seguido
passo a passo, e que poderia, inclusive, ser executado por outra
pessoa (ou por uma máquina).
Tomemos o primeiro exemplo apresentado. Suponha que você
tenha um colega que é o mais novo cliente de seu banco, e ele quer
aprender a sacar dinheiro no caixa eletrônico. Você já tem uma
solução para o problema dele, então resolve ensiná-lo a sacar
dinheiro. Para tanto, você escreve no papel as seguintes instruções:

1. Passe o cartão do banco no leitor.

2. Digite a senha da conta-corrente.

3. No menu que aparecer, escolha a opção Retirada.

4. Digite o valor desejado e aperte a tecla Entra.

5. Confirme o saque e aperte a tecla Entra.

6. Fique esperando em frente ao caixa até sair o dinheiro.

7. Retire o dinheiro.

Dessa forma, se seu colega seguir o algoritmo para sacar dinheiro


ordenadamente, e sem pular nenhum passo, conseguirá ter as
notas em mãos.
Há um outro ponto a considerar: você não tem certeza se seu
colega já recebeu o cartão de uso. Neste caso, decide completar
seu algoritmo com mais uma informação:
1. Passe o cartão do banco no leitor. Se não tiver cartão, digite o
número da agência e o número de sua conta-corrente.

2. Digite a senha da conta-corrente.

3. No menu que aparecer, escolha a opção Retirada.

4. Digite o valor desejado e aperte a tecla Entra.

5. Confirme o saque e aperte a tecla Entra.

Com essa nova versão do algoritmo, seu colega poderá fazer o


saque desejado, tendo ou não um cartão. Para isso, além de entrar
com os dados e pegar o dinheiro, ele pode decidir entre passar um
cartão ou fornecer os dados de correntista.
Por que é importante construir um
algoritmo?
Analise a Figura 1.1:

Figura 1.1.

Assim, devemos construir um algoritmo para:


Podermos visualizar e verificar, ainda no papel, a solução criada
com lógica de programação, sem nos preocupar com detalhes
computacionais, tornando, com isso, as soluções desenvolvidas
bem mais customizadas, com nível de performance
computacional (projeto de solução de problemas);
Uma vez concebida a solução algorítmica para o problema, esta
pode ser traduzida facilmente para qualquer linguagem de
programação do mercado, sendo agregada de funcionalidades
disponíveis nos diversos ambientes computacionais
(codificação);
Antever possíveis problemas na execução do futuro software;
Executar um planejamento global da solução do problema.

Veja nas Figuras 1.2 e 1.3 um esquema ilustrando as fases de


solução de um problema utilizando algoritmos:
Figura 1.2.

Figura 1.3: Fases de solução de um problema utilizando algoritmos.

Observação: a etapa conhecida como Teste de Mesa


será estudada no Capítulo 4.

Análise preliminar
Entenda o problema com a maior precisão possível, identifique os
dados e os resultados desejados. Este é o estágio do processo no
qual o programador obtém entendimento total do problema antes de
pensar numa solução.

Solução em forma de algoritmo

Desenvolva um algoritmo para resolver o problema. Esta é a fase


predominantemente criativa.

Teste de qualidade (validação)

Execute o algoritmo desenvolvido, com dados de entrada para os


quais o resultado seja conhecido. O ideal é que o universo dos
dados tenha todas as combinações possíveis. Abordaremos no
próximo capítulo como realizar, na prática, o teste de qualidade de
um algoritmo.

Alteração

Se o resultado do teste de qualidade não for satisfatório, altere o


algoritmo e submeta-o a um novo teste.
Não se esqueça de alterar, também, as linhas de comentário
inseridas no algoritmo.

Codificação (produto final)

Com o algoritmo concluído e validado, ele estará pronto para ser


convertido a uma linguagem de programação qualquer, obedecendo
a suas regras de sintaxe (regras de escrita definidas para cada
linguagem de programação), um processo altamente mecânico.
Em um algoritmo, devemos distinguir claramente dois aspectos
complementares: um aspecto estático e um dinâmico. Vejamos:
A formulação de um algoritmo geralmente consiste em um texto
contendo comandos (instruções) que devem ser executados
numa ordem prescrita. Esse texto é uma representação
concreta do algoritmo e tem um caráter evidentemente estático,
atemporal, expandindo somente no espaço (da folha de papel);
Por outro lado, o texto escrito não nos interessa em si, mas sim
os efeitos que ele pode evocar na execução no tempo, dado um
conjunto de “valores iniciais”. Cada execução de um algoritmo é
um evento dinâmico, evoluindo no tempo.

A grande dificuldade na concepção e no entendimento de


algoritmos é o problema do relacionamento desses aspectos, ou
seja, como entender (“visualizar”) as estruturas dinâmicas das
possíveis execuções do algoritmo a partir da estrutura estática de
seu texto.
Poderíamos, agora, fazer uma analogia sobre tudo o que foi
estudado quanto à escrita de um algoritmo computacional,
comparando a atividade de solucionar um problema computacional
(sistema de informação) por um analista de sistemas com a
construção de uma casa por um engenheiro civil. Observe as
Figuras 1.4 e 1.5 e compare as atividades:

Figura 1.4: Construção de uma casa.

Figura 1.5: Construção de uma SI.


Ambas as atividades necessitam de planejamento e testes com os
clientes ainda no papel, para que se possa garantir uma boa
solução do problema requerido. Planejamento é tudo! É vital para a
realização de qualquer atividade.
De que maneira representamos um
algoritmo?
Convém enfatizar, mais uma vez, que um algoritmo é uma linha
de raciocínio que pode ser descrita de duas maneiras: na forma
gráfica ou na textual.
As formas gráficas são mais puras por serem mais fiéis ao
raciocínio original, pois substituem um grande número de palavras
por convenções de desenhos. São elas conhecidas como
Fluxograma tradicional, Diagrama de Chapim (NS) e outros. Na
parte textual, temos os pseudocódigos, ou o Portugol.
Cada uma dessas técnicas tem suas vantagens e desvantagens
particulares. Porém, podemos perceber que ambas permitem um
grande nível de clareza quanto ao fluxo de execução. Porém, deve
ser mais difícil entender as representações gráficas do que a de um
algoritmo em sua forma textual. Isso ocorre porque é necessário
conhecer as convenções gráficas de cada uma dessas técnicas,
que, apesar de simples, não são naturais, pois estamos mais
condicionados a nos expressar por palavras.
Outra desvantagem é que sempre se mostra mais trabalhoso
fazer um desenho do que escrever um texto, mesmo considerando o
auxílio de réguas e moldes. A questão é ainda maior quando é
necessário fazer alguma alteração ou correção no desenho. Estes
fatores podem desencorajar o uso de representações gráficas e,
algumas vezes, erroneamente, a própria construção de algoritmos.
Assim, justificamos a opção pelos métodos textuais, que, apesar
de menos puros, são mais naturais e fáceis de usar.
Vejamos um pouco mais sobre cada forma de representar um
algoritmo.

Fluxograma tradicional

Utiliza formas gráficas preestabelecidas na representação dos


algoritmos. O fluxograma é excelente para representar algoritmos
que envolvam ações diferentes para muitas decisões. Veja seus
principais símbolos:

Figura 1.6.

Veja na Figura 1.7 um exemplo de fluxograma:

Figura 1.7.
Diagrama de Chapin

Algoritmos também podem ser escritos por um diagrama,


conhecido como Diagrama de Chapin (ou NS, de Nassi &
Shneiderman), que só permite representar as estruturas de controle
preconizadas, evitando livres transferências.
O mesmo exemplo de algoritmo, visto na página anterior como um
fluxograma, foi representado no diagrama de Chapin:

Figura 1.8.

Pseudocódigo, ou Portugol

O Portugol é uma pseudolinguagem de programação (simbiose do


Português com o Algol e Pascal). A idéia é permitir que, com um
conjunto básico de primitivas instruções, seja possível ao projetista
pensar no problema, e não na máquina que vai executar o
algoritmo, sem, no entanto, ficar muito distante desta mesma
máquina. Em outra perspectiva, que o projetista possa pensar na
solução do problema e que esta solução seja facilmente
implementada no computador.
Então, para representar textualmente algoritmos, usaremos o
português. Porém, não poderíamos utilizar toda a riqueza gramatical
de nossa língua pátria. E para justificar essa dificuldade, apontamos
pelo menos um bom e claro motivo: a ambigüidade.
Vejamos a seguinte frase: “O pregador foi grampeado durante o
conserto”. Essa expressão, quando falada, pode ter até oito sentidos
diferentes, uma vez que o tal “pregador” pode ser o religioso que
prega a palavra de Deus ou um prendedor de roupas; grampeado
pode se tratar de uma escuta telefônica ou do grampo que une
folhas de papel; conserto, quando pronunciado, pode se tratar de
uma apresentação musical (concerto) ou da manutenção de algum
maquinário. Nós até conseguiríamos distinguir qual dos oito sentidos
diferentes se aplicaria, caso avaliássemos a sentença dentro de seu
contexto. Entretanto, o computador é desprovido do raciocínio
necessário parta interpretar a frase.
Para evitar este e outros problemas, utilizaremos um conjunto de
regras que visam a restringir e estruturar o uso do português na
representação dos algoritmos e que, intencionalmente,
aproximamse das linguagens de programação reais (como C e
Pascal), facilitando a futura codificação dos algoritmos. Para tanto,
utilizaremos o Pseudocódigo, que se assemelha bastante com a
forma na qual os programas são escritos. Na verdade, esta
representação é suficientemente geral para permitir que a tradução
de um algoritmo, nela representado, para uma linguagem de
programação qualquer seja praticamente direta.
Para treinar
1. Um rapaz gastou metade de seu dinheiro no lanche e metade
no cinema e ficou com R$ 400,00. Quanto gastou no cinema?

2. Qual o sinal matemático que devemos colocar entre dois e três


para formar um número maior de que dois e menor do que três?

3. O casal Silva tem vários filhos. Cada filha tem o mesmo número
de irmãos e irmãs, e cada filho tem duas vezes mais irmãs que
irmãos. Quantos filhos e filhas o casal tem?

4. Um tijolo pesa um quilo mais meio tijolo. Quanto pesa um tijolo


e meio?

5. Se, de um número de três algarismos subtraímos sete, ele se


tornará divisível por sete. Se subtrairmos oito, ele se tornará
divisível por oito e, se subtrairmos nove, ele se tornará divisível
por nove. Qual é o número?

6. Cinco meninos estavam assistindo à televisão. Eles estavam


sentados em duas cadeiras e três poltronas. Você pode
descobrir onde estavam sentados A, B, C, D e E se você
souber que:
A e B sentavam-se num mesmo tipo de assento;
B e D sentavam-se em tipos diferentes;
D e F sentavam-se em tipos diferentes.
Capítulo 2

Tipos de dados, variáveis,


operadores e instruções

A experiência é como costumamos chamar o conjunto dos nossos


erros.

Oscar Wilde

Dica do autor: o aprendizado de algoritmos não é


fácil, absorve-se o conteúdo apenas por meio de
muitos exercícios. Assim, algoritmos não se aprendem
copiando, ou estudando, mas apenas construindo e
testando.
Tipos de dados primitivos
Para entender tipos primitivos de dados, voltemos nossa atenção
para um conceito muito importante: informação.
Informação é o produto gerado pelos computadores, pois eles são
capazes de manipular e armazenar um grande volume de dados
com alta performance, liberando o homem para outras tarefas, nas
quais seu conhecimento é indispensável. Devemos observar que
existe uma tênue diferença entre dado e informação. Por exemplo,
ao citarmos uma data, como 21 de setembro, estamos
apresentando um dado; ao dizermos que este é o Dia da Árvore,
estamos agregando valor ao dado fornecido, apresentando uma
informação.
Aproximando-nos da maneira pela qual o computador manipula as
informações, vamos dividi-la em seis tipos primitivos, que serão os
tipos básicos usados na construção dos algoritmos.

Inteiro

Inteiro é todo e qualquer dado numérico que pertença ao conjunto


dos números relativos (negativo, nulo ou positivo). Observe:
Número de chamada: 17;
Idade: 25;
Quantidade de dependentes: 3;
Temperatura: -15ºC.

Real

Será todo e qualquer dado numérico que pertença ao conjunto


dos números reais (negativo, nulo ou positivo). Observe:
Altura 1,73 m;
Peso máximo 805,5 kg;
Média final 4,7.
Texto

Será todo e qualquer dado composto por um conjunto de


caracteres alfanuméricos: numéricos (0 … 9), alfabéticos (A … Z, a
… z) e especiais ( #, ?, !, @, &). Observe:
Nome do aluno: “José Francisco”;
Placa: “KTS-4025”;
Telefone: “(21) 2455-2574”.

Observação: todo dado do tipo texto deverá ter sua


atribuição de conteúdo delimitada por aspas (“ “).

Lógico

É todo e qualquer dado que pode assumir duas situações:


verdadeiro/falso, sim/não (biestável). Observe:
Casado: V;
Vazio: F.

Observação: por convenção, as informações do tipo


lógico poderão assumir somente um dos seguintes
valores constantes: verdadeiro (V) ou falso (F).

Data

Todo e qualquer dado armazenado no formato dia/mês/ano.


Observe:
Data de nascimento: 15/05/1980;
Data de admissão: 20/03/2000.
Moeda

Todo e qualquer dado armazenado no formato monetário.


Observe:
Salário por hora: R$ 24,50;
Valor da mensalidade: R$ 400,00.

Exercícios de fixação

Indique para cada dado citado se ele é do tipo Inteiro(I), Real (R),
Lógico (L), Moeda (M) ou Texto (T).

1. O salário do funcionário de uma empresa ( )


2. A quantidade de vendedores em uma loja ( )
3. O número de latas de refrigerante em uma prateleira ( )
4. O nome de um assinante de linha telefônica ( )
5. O preço de um litro de leite ( )
6. O estado de iluminação de uma lâmpada em perfeitas
condições ( )
7. A quantidade de sacos de arroz vendidos em um mercado ( )
8. O preenchimento do sexo de uma pessoa em um formulário ( )
9. A luz de um sinal de trânsito (assuma ‘V’ermelho, ‘A’marelo ou
v‘E’rde) ( )
10. A quantidade de litros de combustível em um posto ( )
11. O número de alunos em uma turma ( )
12. A resposta de uma questão de múltipla escolha (A até D) ( )
13. O CEP em uma carta ( )
14. O valor a pagar pela refeição num restaurante por quilo ( )
15. A placa de um automóvel ( )
16. A quantidade de automóveis produzidos por mês em uma
fábrica ( )
17. O número de bois no pasto de uma fazenda ( )
18. Quantidade de laranjas disponíveis numa caixa ( )
19. O nome do funcionário de um escritório ( )
20. O número de cédulas no caixa de uma agência bancária ( )
21. Quantidade de salas de aula de uma faculdade ( )
22. O status do pagamento de uma fatura (pago/não pago) ( )
23. Um símbolo de “arroba” no endereço eletrônico de um colega ( )
24. A raiz de uma equação do 2º Grau ( )
25. O autor de um livro ( )
26. O saldo bancário de um cliente ( )
27. O número de cheques emitidos por um cliente em um mês ( )
28. Uma letra minúscula do alfabeto ( )
29. O número de canais de um serviço de TV a cabo ( )
30. O valor da prestação de um televisor ( )
Constantes
Entendemos que um dado é constante quando não sofre variação
no decorrer do tempo, ou seja, seu valor é constante do início até o
fim da execução do algoritmo, assim como é constante para
execuções diferentes no tempo. Veja:
Pi: 3,14;
Alíquota de desconto: 10%.
Variáveis de memória
No ambiente computacional, as informações variáveis são
guardadas em dispositivos eletrônicos analogamente chamados
“memória”. De acordo com a Figura 2.1, a seguir, podemos
imaginar essa “memória” como sendo um armário repleto de
gavetas, no qual elas seriam locais físicos responsáveis por
armazenar objetos. Os objetos (que podem ser substituídos) seriam
os dados, e as gavetas, as variáveis.
Visto que na memória (armário) existem inúmeras variáveis
(gavetas), precisamos diferenciá-las, o que é feito por meio de
identificadores (etiquetas ou rótulos). Cada variável (gaveta), no
entanto, pode guardar apenas um dado (objeto) de cada vez, sendo
sempre do mesmo tipo primitivo (material).
Portanto, precisamos definir nomes para determinadas gavetas,
especificando qual o material dos objetos que lá podem ser
armazenados. Em outras palavras, declarar as variáveis que serão
usadas para identificar os dados.

Figura 2.1.

Alguns exemplos seriam a cotação do dólar, o peso de uma


pessoa, o índice da inflação.
Um exemplo para ilustrar a diferença entre valores constantes e
variáveis seria a construção de um algoritmo para calcular o valor da
área de uma circunferência. Naturalmente, teríamos de usar a
fórmula que expressa que a área é igual a Pi multiplicado pelo raio
elevado ao quadrado. Nela, Pi tem o valor constante de 3,1416...,
independentemente de qual seja a circunferência (vale para todas
as ocasiões em que calcularmos a área); já o valor do raio (na
fórmula, r) depende da circunferência que estamos calculando, logo,
é variável a cada execução do algoritmo.
Formação de identificadores de
variáveis e constantes
As variáveis e as constantes de um algoritmo precisam ser
nomeadas e declaradas para que possam ser utilizadas. Tratando-
se de nomeação, existem algumas regras que devem ser seguidas:
Devem ter o nome começado por um caractere alfabético;
Seguindo o primeiro caractere alfabético, podem existir outros,
alfabéticos ou numéricos;
Devem ser usados caracteres especiais, como *, @, !, ?, +, -,
espaço etc.;
Devem ser escolhidos nomes que sejam claros e que possam
demonstrar com facilidade o que as variáveis pretendem
armazenar.

Veja a tabela seguinte:

Identificadores válidos Identificadores inválidos

Profissao Profissão

Bim1 1bim

Nome _ aluno Nome Aluno

Depto _ XY Depto(xy)

Tabela 2.1.
Tendo como objetivo facilitar a compreensão do algoritmo, os
identificadores são escritos, sempre, com letras minúsculas,
enquanto as palavras-chave são escritas com maiúsculas e grifadas
(regra de legibilidade).
Denomina-se palavra-chave aquela que tem um significado
próprio, independente do algoritmo em que esteja inserida. Em vista
disso, as palavras-chave não podem ser usadas como
identificadores.

Observação: para facilitar a nomenclatura de algumas


variáveis, é possível utilizar prefixos padronizados
para as variáveis que podem se agrupar em algumas
categorias predefinidas, de acordo com esta tabela:

Prefixos Exemplos

nu: número num_chamada

cd: código cod_depto

vl: valor valor_custas

dt: data dtNascim

nm: nome NmAluno

Tabela 2.2.
Declaração de variáveis em um
pseudocódigo
Para declarar uma variável ou uma constante em um
pseudocódigo, unimos dois conceitos já vistos: o identificador e o
tipo de dado primitivo com a seguinte regra de sintaxe:
VAR
Identificador1, Identificador2, IdentificadorN : Tipo de dado

CONST
Identificador1, Identificador2, IdentificadorN : Tipo de dado

Vejamos um exemplo:
VAR
Nome : TEXTO
Salario _ familia : REAL
Codigo, quantidade, matricula : INTEIRO

CONST
Pi : REAL
Atribuição de um dado a uma
variável
Antes de explicar o que vem a ser um comando de atribuição,
devemos definir o conceito de comando como a descrição de uma
ação a ser executada em dado momento.
O comando de atribuição permite que se forneça um valor a uma
variável, sendo que a natureza deste valor tem de ser compatível
com o tipo da variável na qual está sendo armazenado o comando.
Qualquer tentativa de atribuição de valores de outro tipo é
considerada um erro:
Identificador Expressão

em que:

É o nome da variável à qual está sendo


Identificador
atribuído o valor.

É o símbolo utilizado para a atribuição.

Valor, expressão ou variável que atribuirá o


Expressão
valor ao identificador.

Tabela 2.3.

Quando atribuímos o resultado da expressão a uma variável,


devemos atentar para os seguintes pontos:
A expressão pode incluir números, operadores, parênteses e
nomes de variáveis;
O resultado da expressão precisa ser compatível com o tipo da
variável que vai guardá-lo. Não podemos, por exemplo, ter um
resultado 9,43 e querer armazená-lo em uma variável do tipo
inteiro;
À esquerda da “setinha”, só podemos ter um único nome de
variável, e que já tenha sido declarado anteriormente no
algoritmo. É ela que vai guardar o resultado da expressão;
É comum entender o comando de atribuição como se a seta
representasse a palavra “recebe”. Desse modo, podemos ler
uma atribuição como: “uma variável recebe o resultado de uma
expressão”.

Exemplos:
• nu _ chamada 1;
• cor “VERDE”;
• existe Falso;
• A B;
• media soma / n.

Devo frisar, neste ponto, que ao atribuirmos um novo valor a uma


variável que já possua um valor armazenado, seu valor será perdido
e o novo valor ficará sendo seu conteúdo atual. Veja:
Nome “Aula de Algoritmos” .... Nome “Informática”

O valor atual da variável Nome será o texto “Informática”, pois o


valor anterior foi imediatamente perdido quando se atribuiu este
novo valor. Vale lembrar que todo algoritmo será sempre executado
seqüencialmente de cima para baixo e da esquerda para a direita.

Desafio

Considere um algoritmo iniciado pelas seguintes linhas de


comando:
Valor1 1
Valor2 2
Valor3 3

Continue sua execução (usando somente o comando de


atribuição entre variáveis) e faça que, no final do algoritmo, o
conteúdo armazenado em Valor1 seja igual a 3, o conteúdo de
Valor2 seja 1 e o conteúdo da variável Valor3 seja 2.
Comentários
A esta altura, o leitor já percebeu a preocupação existente com a
clareza do algoritmo, ou seja, o grau de facilidade que as pessoas
terão em compreender o que nele está descrito.
Um instrumento de grande valia usado para essa finalidade
denomina-se comentário. Ele é um texto, ou simplesmente uma
frase, que aparece sempre delimitado por chaves ({comentário}).
Os comentários podem ser colocados em qualquer ponto do
algoritmo no qual se façam necessários.
No exemplo a seguir, é mostrado um conjunto de declarações em
que foram introduzidos comentários com o intuito de explicar o
significado de cada uma das variáveis:
VAR {área de declaração das variáveis}
MAT: INTEIRO {número de matrícula do aluno}
COD: INTEIRO {código do curso}

Observação: todo algoritmo deve conter comentários,


a fim de que as pessoas possam entendê-lo mais
facilmente.
Identação
Para que haja melhor legibilidade na leitura dos comandos de um
algoritmo é interessante que se utilize um recuo (tabulação) para a
margem direita do algoritmo, recurso chamado identação.
Permitimos com isso maior clareza quanto à observação das
estruturas que contêm outras estruturas em seu interior
(embutimento).
Operadores
Operadores aritméticos

Denominam-se operadores aritméticos os símbolos utilizados em


uma expressão aritmética e cujos operandos são constantes, ou
variáveis do tipo numérico. Vejamos estes operadores:

Operador Significado

+ Adição
- Subtração
* Multiplicação
/ Divisão
** ou ^ MENOS O OU Potenciação

Tabela 2.4: Operadores aritméticos.

Observação: para calcular a raiz quadrada de


números inteiros será utilizada a função predefinida
denominada RAIZ( ), cujos detalhes de implementação
serão vistos a seguir.

A notação utilizada para expressões aritméticas nos algoritmos é,


basicamente, a mesma da Matemática, com as seguintes restrições:
Não é permitido omitir o operador de multiplicação, o que é
comum nas expressões matemáticas. Isso evita a confusão
quanto aos nomes de variáveis, pois numa expressão com a
forma AB + C, como saber se AB é o nome de uma variável ou
a multiplicação entre os conteúdos de duas variáveis?
Nas expressões aritméticas, as operações guardam entre si
uma relação de prioridade, tal como na Matemática. Veja:
Prioridades Operação

Primeiro lugar Potenciação


Segundo lugar Multiplicação, divisão
Terceiro lugar Adição, subtração

Tabela 2.5: Prioridades das operações.

Para obter uma seqüência de cálculo diferente, vários níveis de


parênteses podem ser usados para quebrar prioridades definidas.
Não é permitido o uso de colchetes e chaves, uma vez que estes
símbolos são utilizados nos algoritmos para outras finalidades.
Por questão de uniformidade, adota-se um único símbolo para
cada um dos operadores. Assim sendo, não é permitido o uso do
símbolo “÷” e o “ponto” para indicar divisão e multiplicação,
respectivamente.

Operadores relacionais

São os símbolos utilizados entre dois valores do mesmo tipo, e


que têm como resultado um valor lógico (veja explicações sobre
operadores lógicos após este tópico).
Os seus operadores são:

operador significado

= Igual

> Maior que

< Menor que


<= Menor ou igual

>= Maior ou igual

<> Diferente

Tabela 2.6: Operadores relacionais.

Veja um exemplo:
Nota 6,5
Média 7,0

Expressão Resultado

Nota < Média Verdadeiro

Nota >= Média Falso

Nota < > Média Verdadeiro

Tabela 2.7: Resultados de expressões relacionais.

Operadores lógicos

São utilizados três conectivos básicos, que permitem, inclusive, a


formação de novas proposições lógicas a partir de outras
proposições lógicas mais simples.
Os conectivos lógicos são:
NÃO, para negação, em primeiro lugar;
E, para conjunção, em segundo lugar;
OU, para disjunção, em terceiro lugar.

O resultado obtido pela avaliação de uma expressão lógica ou


relacional é sempre um valor lógico, isto é, um valor falso ou
verdadeiro.
Tabelas-verdade
Tabelas-verdade são um conjunto de todas as possibilidades
combinatórias entre os valores de diversas variáveis lógicas, as
quais se encontram em apenas duas situações (verdadeiro ou
falso), e um conjunto de operadores lógicos.
Construiremos uma tabela-verdade com o objetivo de dispor de
uma maneira prática os valores lógicos envolvidos em uma
expressão lógica:
Operação de conjunção/operador E lógico. Exemplo para duas
variáveis lógicas V1 e V2:

V1 E V2

V V V

V F F

F F V

F F F

Tabela 2.8: Tabela-verdade do operador E.


Operação de disjunção/operador OU lógico. Exemplo para duas
variáveis lógicas V1 e V2:

V1 OU V2

V V V
V V F

F V V

F F F

Tabela 2.9: Tabela-verdade do operador OU.

Operação de negação/operador NÃO lógico. Exemplo para a


variável lógica V1:

V1 NÃO V1

V F

F V

Tabela 2.10: Tabela-verdade do operador NÃO.

A partir desses exemplos, tiramos as seguintes conclusões:


Com o operador E, o resultado da avaliação de uma expressão
lógica somente será verdadeiro se ambas as condições forem
verdadeiras;
Com o operador OU, o resultado da avaliação de uma
expressão lógica somente será falso se ambas as condições
forem falsas.
Funções em um algoritmo
As funções empregadas no desenvolvimento de algoritmos
computacionais nada mais são que algoritmos pré-desenvolvidos,
que permitem solucionar pequenos problemas, ou seja, seria o
mesmo que um pedaço de algoritmo desenvolvido por alguém que
vai permitir que você escreva seus algoritmos mais facilmente, sem
que você precise “reinventar a roda”, perdendo tempo criando uma
lógica que já existe, por meio de uma função.
Existem funções lógicas, de texto e numéricas. Além das
operações aritméticas básicas, anteriormente citadas, podemos usar
nas expressões aritméticas (EA) algumas funções muito comuns na
Matemática.
Nas tabelas seguintes, encontram-se algumas das principais
funções existentes e o resultado fornecido por cada uma delas:

Função Descrição

Calcula a raiz quadrada ou


Raiz(num)
exponencial de um número.

Retorna o resto da divisão de num1


Resto(num1,num2)
por num2.

Retorna o valor do quociente de


Quociente(num1,num2)
num1 por num2.

Log(num) Logaritmo na base 10 de num.

ABS(num) Valor absoluto de num.


Trunca(num) Retorna a parte inteira de um
número fracionário.

Transforma por arredondamento


Arredonda(num)
um número fracionário em inteiro.

Retorna a quantidade de
Tamanho(exp)
caracteres de uma expressão texto.

Tabela 2.11: Tabela de funções computacionais: categoria


numérica.

Função Descrição

Retorna uma quantidade de


Esquerda(exp,qtde) caracteres mais à esquerda de uma
expressão texto.

Retorna uma quantidade de


Direita(exp,qtde) caracteres mais à direita de uma
expressão texto.

Retorna uma quantidade de


caracteres de uma expressão texto
Cadeia(exp,inicio,qtde)
a partir de uma posição inicial
definida.

Tabela 2.12: Tabela de funções computacionais: categoria texto


(string).
Função Descrição

Retorna verdadeiro ou falso quando o final


Fim(arquivo)
do arquivo/tabela é encontrado.

Retorna verdadeiro ou falso quando o início


Início(arquivo)
do arquivo/tabela é encontrado.

Retorna verdadeiro ou falso se um arquivo


Vazio(arquivo)
estiver ou não vazio (sem registro).

Tabela 2.13: Tabela de funções computacionais: categoria lógica.

A função atua sobre um agrupamento de texto ou numérico, que é


o resultado obtido após a avaliação da expressão aritmética entre
parênteses.
As palavras que designam as funções são escritas com letras
maiúsculas e sublinhadas.
Vale ressaltar que a sintaxe de uma função sempre terá:
Parênteses, nos quais será(ão) inserido(s) o(s) parâmetro(s)
necessário(s) para seu funcionamento;
A atribuição a uma variável de retorno, que será responsável
por armazenar o valor processado pela função.

Veja a sintaxe:
VarRetorno FUNÇÃO(P1,P2,Pn)

Em que:
VarRetorno é uma variável de memória que receberá o valor de
retorno ao processamento da função, seu tipo de dado tem de
ser compatível com o tipo de dado de retorno da função;
P1, P2, Pn são parâmetros que serão fornecidos à função,
necessários à sua finalidade e funcionamento, sempre
separados por vírgula.
Prioridade dos operadores
Pode-se ter mais de um operador na mesma expressão. Em
alguns casos, conforme os valores envolvidos, a ordem em que são
efetuadas as operações afeta o resultado final.
Assim como acontece entre as operações aritméticas, também
existe uma relação de prioridade entre todos os operadores. Na
tabela seguinte, são apresentadas as prioridades entre todos os
operadores conhecidos, visto que podem estar presentes em uma
mesma expressão lógica:

Prioridades Operadores

Primeiro lugar Função

Segundo lugar Aritmético

Terceiro lugar Relacional

Quarto lugar NÃO

Quinto lugar E

Sexto lugar OU

Tabela 2.14: Tabela de prioridade de operadores.

Observação: os parênteses mudam a prioridade de


qualquer operador.
Exercícios de fixação

Resolva as expressões seguintes:


1)
A V
B F
C 2
D 100
E D ^ 2 >10 ou NÃO A
F E ou B e A e NÃO C > 100
G E ou D > D ^ C e B ou NÃO A
C ( (D + D) / C ^ C * 2) -10
H (C – C ^ 0+C * 2) > 5

2)
J ”INFO/2006-ATUAL”
W 6
A ESQUERDA (J , W) = “INFO/2”
B QUOCIENTE (TAMANHO( J ) ,2)
C DIREITA (SUBCADEIA(J , 5 , 6) , 2)
D RESTO (QUOCIENTE (TAMANHO(J)*2,3),TAMANHO(ESQUERDA(J,2))
D TAMANHO( J ) > RAIZ (TAMANHO (J+10) ) e W / 2 <>TAMANHO(J)
^ 2

3)
A RAIZ (121)
B A ^ 0
C B > A e A > B ou A <> B e B <> A ou A = B
C NÃO C
C NÃO (NÃO C)
A 10 ^ 2 + (( A / B * 2) -20 / 2 / 2 / 2)

4)
D 1
E “MIRIAN”
G “SALA DE AULA”
F SUBCADEIA (G, 3, 5) + DIREITA (E, 3)
H (TAMANHO (F) ** D) * 2 < QUOCIENTE (36, TAMANHO (E))

5)
A 3
B 6
C B < A E (TAMANHO (B) * 2 / 4 < 10
D RAIZ (TAMANHO (B) ^ 2) = B E A ^ 3 <> RESTO (8 * 2, 4) OU
C

6)
A RAIZ (36)
B 2
C A ^ B >= B / 2 E A > B OU B <> A
G C= A OU B < A E (A ^ B / 2) < B

7)
A RAIZ (144) + 6
B ((A ^ 2) – 24)) / 3
C B * 2 / 4 > A OU QUOCIENTE (18, 6) <> A E NÃO V

8)
A “COMPUTADOR 10”
B RESTO (TAMANHO(A) – 1, 2) > 10 E SUBCADEIA (A, 2, 2) =
DIREITA (A, 6)

9)
A “BRASIL”
B “SUDESTE”
C “PRAIA”
D SUBCADEIA (B, 3, 2) + DIREITA (A, 3) + ESQUERDA (C, 2) =
ESQUERDA (A, 4)
E NÃO D

10)
A RAIZ(144)
B 3
C B ^ 2 + ((A + B) / 3) > (A - B)/3
C NÃO C
C NÃO ( NÃO C)

11)
A 10
B “PROGRESSO”
C (TAMANHO (B) / 2 ^ 5 > 100 OU ( RAIZ (49)) < > A > OU
QUOCIENTE ( 20, A) > TAMANHO (B)
E NÃO (D) E NÃO ( NÃO (C)) OU (TAMANHO (SUBCADEIA (B,5,2)))
> ( TAMANHO (CADEIA (B,2,2)))

12)
A 2
B 3
C B ^ A / 3 + B * 2 ^ 2
D V
E F
H A < 10 + A * C E D OU C > B ^ 3

13)
A 2
B 4
C B ^ 2 / A * 4 + B
D F
E V
H A > 10 E D OU B ** 2 < = 6 E C < > 4

14)
K “LAGOA”
L “PATO”
M DIREITA (K,2) + ESQUERDA (L,3) = “LAGO”
N M OU NÃO M E (TAMANHO (K) > TAMANHO (L))

15)
B 4
C “INFORMÁTICA”
D (TAMANHO (C) 12 / 11 < QUOCIENTE ( 10,B)
E DIREITA (C,4) + ESQUERDA (C,2) = “FOR”

16)
J “MURILO – OE”
K “MARQUES – EST”
Y “2”
A (TAMANHO (K) ^ 2) < > 3 E SUBCADEIA (J, 1,2) = “MU”
B RESTO (TAMANHO (J) / 3,2) > QUOCIENTE (8,Y)

17)
A RAIZ (36)
B “EDGAR”
S “CASSIANA”

B ESQUERDA (K,2) = S OU DIREITA (5,3) < > “CA”


C A = QUOCIENTE (TAMANHO (S) * 2,2) E NÃO B

18)
X 4
Y 6
A ”CELESTE”
B “TEDIEL”
C (TAMANHO (A + B) + X * Y – QUOCIENTE (12,X) ^ RESTO (Y,2))
D CADEIA (A,2,3) = ESQUERDA (CADEIA (B,4,2),1) E TAMANHO (A
- B) = QUOCIENTE (C + B,4) – RESTO (X,3)

19)
A 10
B QUOCIENTE (A,2)
C RESTO (B,2)
D B > A OU (A – C * B) > C

20)
A RAIZ (25)
B A ^ 2
C “RAFAEL”
D CADEIA (C,3,4) < > B E ESQUERDA (C,2,5) = A

21)
A RAIZ (36)
B 5 + A ^ 0
C A > = B OU B = < A E B < > A E A > B
C NÃO C
C NÃO (NÃO C)

22)
A “CURSO TÉCNICO INFORMÁTICA”
B 8
C ESQUERDA (A,B)
D CADEIA (A,8,8)
E TAMANHO (A ^ 2) / 5 = A E QUOCIENTE (C * D ^ 2) / 2 = D
23)
A 25
B 8
C “TÉCNICO ADMINISTRAÇÃO”
D “TÉCNICO ENFERMAGEM”
E TAMANHO (A ^ 2) / 5 = TAMANHO A E QUOCIENTE (TAMANHO (C +
D ^ 2) / 2 = TAMANHO D)
F B > A E A > B OU A < > B E B <A

24)
A 20
B 12
C “LIVIA”
D RESTO (20,3) < > B E QUOCIENTE (TAMANHO (C) ^ 2 , 4) =
TAMANHO (DIREITA (C,2))

25)
C TAMANHO (CADEIA (C,1,2) + DIREITA (C,3) < 6 OU RAIZ (100)
+ QUOCIENTE (TAMANHO (C) / 2) – RESTO (13,3)) = 10

26)
H 5
M 2
A “LUCIO”
B “PRISCILA”
H QUOCIENTE (TAMANHO (A + B), 13)
B TAMANHO (A) ^ 2 / 5 > QUOCIENTE (10,M)
M ESQUERDA (B + A,3) + DIREITA (A + B,5)

27)
A 2
B A ^ 2
C 10
D RESTO ((C + B + A),2) + QUOCIENTE (( C + B+ A),2) > (A +
B) * 10 ** 0

28)
A1 “GRUPO SEIS”
B1 ESQUERDA (A1,5) < > DIREITA (A1,4) E SUBCADEIA (A1, 4,4)
< > “POSE”
A ESQUERDA (A1,5) = DIREITA (A1,4) OU SUBCADEIA (A1,4,4 ) =
“POSE”
C NÃO (NÃO A = NÃO B1) E A = B1 OU B1 = A

29) Se A = 127, B = 10, C = 5, D = Falso e E = Verdadeiro, qual é


o valor produzido por cada uma das sentenças abaixo?

a. Não D
b. D e E
c. (D e E) ou (A = B)
d. (D ou E) e (A < B)
e. (A > B) ou (B < C)
f. Não (A < B)
g. A + B < C e D ou E e Não D
h. A + B * C / B = 3 e Não (A ou B)
Como representar um algoritmo
em pseudocódigo?
Para representar um algoritmo em pseudocódigo, utilizaremos um
formato padrão que facilita muito a passagem, posteriormente, da
solução desenvolvida, para uma linguagem de programação
estruturada.
Vale ressaltar que abordaremos, nos próximos capítulos, uma
outra metodologia, que será aplicada para as implementações dos
pseudocódigos em linguagem de programação visual.
O formato-padrão pode ser visto a seguir:
ALGORITMO nomedoAlgoritmo
VAR
NomeVar : TIPODEDADO
CONST
NomeConst : TIPODEDADO
INICIO

BLOCO DE COMANDOS
FIM

FIM ALGORITMO

em que:
ALGORITMO: é uma palavra reservada que indica o início da
definição de um algoritmo em forma de pseudocódigo;
nomedoAlgoritmo: é um nome simbólico dado ao pseudocódigo
com a finalidade de distingui-lo dos demais;
VAR e CONST: consiste em uma área reservada para declarar
as variáveis “globais” e constantes usadas no pseudocódigo
principal;
INICIO e FIM: são as palavras que delimitam o corpo do
pseudocódigo.

Observação: o pseudocódigo sempre vai possuir, em


seu bloco de comandos (corpo do pseudocódigo),
instruções, que se dividirão em entrada de dados
(LEIA), processamento e saída de dados (MOSTRE,
IMPRIMA).
Instruções primitivas
Instruções de entrada de dados

Permite ao algoritmo obter dados do ambiente externo, como, por


exemplo, por meio do teclado, os quais serão armazenados
temporariamente em uma variável de memória, tornando os
algoritmos mais genéricos do que se fixássemos um valor inicial
para o algoritmo para que ele sempre processasse e exibisse os
mesmos valores.
Em termos de sintaxe, temos três opções distintas:
Quando queremos apenas representar qual variável estamos
lendo:
LEIA variável

Quando queremos inserir um cabeçalho junto à leitura da


variável (recomendado):
LEIA “Texto Descritivo”, variável

Quando queremos ler mais de uma variável ao mesmo tempo:


LEIA variavel1, variavel2, variavelN

Quando o algoritmo encontra um comando de leitura, ele fica


aguardando até que o usuário digite um valor, que deverá ser
correspondente ao tipo de dado atribuído à variável contida no
comando LEIA.
Este comando envia o dado lido pelo teclado para a memória
principal do computador no local correspondente à variável
informada pelo comando:
ALGORITMO Exemplo _ Leia

VAR
horas _ trab: INTEIRO
sal _ hora, sal _ receber : REAL
INICIO
LEIA horas _ trab
LEIA sal _ hora
sal _ receber horas _ trab * sal _ hora
FIM

FIM ALGORITMO

Instruções de saída de dados

Permite ao algoritmo, após obter o resultado de um


processamento, fornecer, em periféricos de saída, informações ao
ambiente externo sobre o conteúdo de uma variável armazenada na
memória principal, ou do próprio processamento realizado. Veja:

Se a saída for no vídeo Se a saída for na impressora

MOSTRE var1,var2,varn IMPRIMA var1,var2,varn

MOSTRE var1 IMPRIMA var1

MOSTRE “Texto”, var1 IMPRIMA “Texto”, var1

Tabela 2.15: Tabela de instruções de saída de dados.

Observação: a opção “Texto”, prevista no formato da


instrução, permite, também, que sejam explicitados
textos para a documentação das saídas ou exibição
de mensagens.

No lugar das variáveis, a instrução de saída permite que seja


inserido o conteúdo de uma expressão aritmética. Veja:
MOSTRE “A média do aluno é “, (n1+n2)/2
Veja:
ALGORITMO Exemplo _ Mostre

VAR
horas _ trab: INTEIRO
sal _ hora, sal _ receber : REAL
INICIO
LEIA horas _ trab
LEIA sal _ hora
sal _ receber horas _ trab * sal _ hora
MOSTRE “O funcionário vai receber”, sal _ receber
FIM

FIM ALGORITMO
Para treinar
1. Dois homens vão fazer uma viagem de 18.000 km, de
automóvel. Entretanto, os pneus só agüentam 12.000 km.
Quantos pneus, no mínimo, precisam levar de reserva?

2. Um homem estava morrendo e sua mulher estava prestes a ter


criança. No testamento, deixou 2/3 de seus bens para o filho
(se fosse homem) e 1/3 para sua mulher. Se a criança fosse
mulher receberia apenas 1/3 e a esposa 2/3. Após sua morte, a
mulher deu à luz a gêmeos, um menino e uma menina. Como
pode o juiz dividir o dinheiro de acordo com o desejo do morto?

3. Paulo e Roberto estavam indo de bicicleta de uma cidade para


outra, 20 km distante. Após viajarem 4 km, a bicicleta de
Roberto quebrou. Eles queriam chegar rápido, juntos, andando
o mínimo a pé. Podiam andar à velocidade de 4 km/hora e, de
bicicleta, à 8 km/hora. Combinando andar a pé e de bicicleta,
como atingiriam seu objetivo, para cobrir os 16 km restantes?

4. Três homens querem atravessar um rio. O barco que possuem


tem a capacidade máxima de 150 quilos. Eles pesam 50, 75 e
120 quilos. Como podem atravessar sem afundar o barco?

5. Todos os sábados, à tarde, encontro-me com uma amiga. A


hora prevista para o encontro é meio-dia. Na primeira vez, ela
chegou às 12h30. No sábado seguinte, às 13h20. No outro
sábado, às 14h30. E, no outro, às 16:00. A que horas chegará
na próxima semana?

6. Escreva, dentro dos parênteses, as palavras que faltam:

BRANCO (NELA) LUZES RUMO (ERMA) PENA


FIASCO (..............) LICOR ISTO (..............) CRIO
Capítulo 3

Estruturas de controle de um
algoritmo

A melhor forma de prever o futuro é criá-lo.

Peter Drucker
Estruturas de controle de um
algoritmo
A utilização de estruturas de controle sobre um algoritmo permite
a execução automática de seus passos (comandos) predefinidos, e
permite, ainda, que o algoritmo possa “decidir” entre dois ou mais
valores em uma condição, ou testar várias condições paralelas para
o conteúdo de uma variável lida.
O estudo dessas estruturas permite ao programador resolver
qualquer problema em pseudocódigo aplicado à computação.
Essas estruturas serão apresentadas simultaneamente em
pseudocódigo, NS e no fluxograma tradicional, para que o leitor
tenha uma visão global da aplicação de cada estrutura existente nos
três tipos de representação de algoritmos.

Estrutura seqüencial

Este tipo de estrutura possui a característica de ter cada comando


executado após, e somente após, o término do passo anterior.
Quando concluída determinada linha de comando, seu comando
posterior será sempre executado, necessariamente. Cada comando
é executado uma, e somente uma, vez.
Os comandos são sempre executados de cima para baixo e da
esquerda para a direita. O conjunto desses comandos é
denominado bloco de instruções.
Quase 100% dos problemas são resolvidos com a aplicação
dessa estrutura.
Sintaxe da estrutura seqüencial:
Figura 3.1.

Exercício resolvido

Elabore um algoritmo que calcule a área de um retângulo


qualquer, recebendo, para isso, o valor de sua base e de sua altura,
em centímetros.
Algoritmo calcarea
Var
area, base, alt : real
início
Leia base, alt
area base * alt {form. para cálculo da área do retang}
mostre “O valor da área do retângulo é ‘, área
fim

Estrutura condicional

Condicional simples
Este tipo de estrutura permite executar uma seqüência de
comandos de acordo com o resultado de uma comparação
(condição) ou de uma decisão.
Após a avaliação da condição, uma das alternativas é executada.
Sendo executada a alternativa ENTÃO, quando a condição for
satisfeita (verdadeira), ou a alternativa SENÃO, quando a condição
não for satisfeita (falso):
SE condição ENTÃO
Comando1
Comando2
.
.
.
ComandoN

SENÃO

Comando1’
Comando2’
.
.
.
ComandoN

FIM SE

Observação: a cláusula SENÃO é opcional, ou seja,


posso testar, para determinada condição, os valores
verdadeiros (ENTÃO) e falsos (SENÃO), ou posso
optar, de acordo com o previsto para solucionar o
problema, por testar o resultado verdadeiro.

Uma condição pode ser formada com a união de duas ou mais


condições, sendo, para isso, conectadas entre si com o uso de um
operador lógico E/OU:
SE nota > 7,0 E bim = 4 E disciplina = “Inglês” ENTÃO
..................

Observe a sintaxe para o primeiro caso, sem a opção para a


condição falsa:
Figura 3.2.

Agora, a sintaxe do segundo caso, com a opção para a condição


falsa:

Figura 3.3.

Condicional composta
Certas aplicações envolvem um grande número de testes
condicionais simples e, para tratá-los, é preciso aninhamento de
partículas SE. Existe uma estrutura que, aplicada a certos casos de
aninhamento, produz o mesmo resultado e torna o algoritmo mais
inteligível e menos vulnerável a ambigüidades. Esta estrutura é
chamada estrutura condicional composta. Observe:

Figura 3.4.

Observação: a opção contrária somente será


executada se a condição testada não for satisfeita por
nenhuma condição acima da cláusula DO
CONTRÁRIO.

Não existe limite para o número de condições a serem testadas,


nem para o número de instruções por condição.

Exercício resolvido

Elabore um algoritmo que receba dois números pelo teclado e


informe se ambos são iguais. Caso não sejam, informe qual é o
maior dos dois números.
Algoritmo maior _ de _ dois _ numeros
Var
Num1, num2 : inteiro
início
Leia num1, num2
SE num1 = num2 ENTÃO
Mostre “os números informados são iguais !”
SENÃO
SE num1 > num2 então
Mostre “o primeiro número é o maior”
SENÃO
Mostre “o segundo número é o maior”
FIM-SE
FIM-SE
fim

Exercício resolvido

Veja a elaboração de um algoritmo que recebe o salário bruto de


um funcionário e calcule seu desconto de INSS de acordo com sua
faixa salarial:

Faixa salarial Porcentagem de desconto

Menos de R$ 500,00 5%

De R$ 501,00 a R$ 1.200,00 7%

De R$ 1.201,00 a R$ 1.800,00 9%

Acima de R$ 1.801,00 10%

Tabela 3.1.
Algoritmo calc _ desc _ inss
Var
Salar _ liq : moeda
início
Leia salar _ liq
Selecione Caso
Caso salar _ liq <= 500,00
Salar _ liq salar _ liq * 0,95
Caso salar _ liq > 500,00 E salar _ liq <= 1200,00
Salar _ liq salar _ liq * 0,93
Caso salar _ liq > 1200,00 E salar _ liq <= 1800,00
Salar _ liq salar _ liq * 0,91
Do contrário
Salar _ liq salar _ liq * 0,90
Fim-selecione
Mostre “O Salário líquido do funcionário será “, salar _
liq
Fim

Estruturas de repetição

São estruturas utilizadas quando há necessidade de que um


conjunto de comandos seja executado repetidamente (laço),
enquanto determinada condição permanecer válida (expressão cujo
resultado é o valor lógico Verdadeiro).

Repetição com teste no início


Permite que um bloco de comandos seja executado enquanto a
condição definida no início da estrutura permanecer verdadeira, ou
seja, ela somente executa o bloco de instruções se a condição
estiver verdadeira pelo menos da primeira vez em que for
encontrada pela seqüência lógica do pseudocódigo.
Veja como funciona a estrutura:
Uma condição qualquer é testada. Se a condição for
verdadeira, os comandos são executados, e o controle retorna
para o ENQUANTO, que testa novamente a condição, repetindo
todo o ciclo, até que a condição seja falsa;
Se já da primeira vez o resultado da condição for falso, os
comandos não serão executados nenhuma vez;
Se a condição for falsa, o controle passa para a primeira
instrução após o FIM-ENQUANTO.

Esse ciclo de instruções executado repetidas vezes chama-se


loop (laço).
Sempre que usar esse tipo de estrutura, deve ter a certeza de que
existe a possibilidade De a condição não ser satisfeita (ou seja, de
ser falsa), caso contrário, corre-se o risco de a execução do
pseudocódigo ficar presa num loop infinito.
Veja a sintaxe da estrutura de repetição com teste no início:

Figura 3.5.

Repetição com teste no final


Permite que um bloco de comandos seja executado
repetidamente enquanto uma condição qualquer NÃO for
verdadeira, sendo que primeiro são executados os comandos e,
depois, é realizado o teste da condição.
Se a condição estiver verdadeira na primeira vez que a estrutura
for encontrada pela seqüência lógica do pseudocódigo, pelo menos
uma vez o bloco de comando será executado, pois, como já foi dito,
o teste lógico encontra-se no final da estrutura. Essa estrutura tem a
mesma função da anterior; a maneira de montar a condição é que
se diferencia.
Veja como funciona a estrutura:
Primeiro, os comandos são executados;
Quando o controle do algoritmo atinge a cláusula ATE QUE,
uma condição qualquer é testada; se a condição for verdadeira,
o controle passa para o primeiro comando após o ATE QUE;
Se a condição for falsa, o controle retorna para o REPITA, para
que o ciclo seja novamente executado, até que a condição seja
verdadeira.
Esta estrutura é muito similar à estrutura de repetição com teste
no início, sendo sua principal diferença percebida quando o fluxo de
execução do algoritmo encontra a estrutura de repetição, e sua
condição de repetição NÃO é satisfeita.
A repetição com teste no início NÃO não executará o bloco de
comandos, mas a estrutura de repetição com teste no final
executará pelo menos uma vez o bloco de comandos, como já foi
dito anteriormente.
Observe a sintaxe da estrutura de repetição com teste no final:

Figura 3.6.

Repetição com variável de controle


Para algumas aplicações, o controle condicional de laços imposto
pelas estruturas ENQUANTO e REPITA é desnecessariamente
complicado. Em muitos casos, simplesmente, pode-se desejar
executar um bloco de comandos, um número predeterminado de
vezes, mediante o uso de uma variável que serve como
ACUMULADOR:
Veja a sintaxe em pseudocódigo:
PARA <Variável> DE <Início> ATÉ <Fim> [PASSO<Valor>] FAÇA
<Comando1>
<Comando2>
<Comando3>
<ComandoN>
FIM-PARA
Em que:
<variável>: variável de memória predefinida, servindo como
acumulador;
<Início>: valor inicial de <Variável>, podendo ser outra variável,
uma constante ou uma expressão aritmética;
<Fim>: valor final de <Variável>, que limita o número de
execuções, podendo ser outra variável, uma constante ou uma
expressão aritmética;
<Valor>: valor que deve ser somado ou subtraído de
<Variável>, podendo ser outra variável, uma constante ou uma
expressão aritmética.

Caso se omita o incremento (PASSO<Valor>), que é opcional, a


variável será incrementada da unidade. Para qualquer incremento
diferente de uma unidade, é obrigatório constar a cláusula PASSO.
Esta estrutura não precisa que a variável acumuladora seja
incrementada pelo programador, pois a própria estrutura realiza
essa tarefa automaticamente.
Veja como funciona a estrutura:
Na primeira passagem, <Variável> recebe o valor de <Início> e
são executados os comandos;
Ao atingir a cláusula FIM-PARA, o valor de <Valor> é somado à
<Variável> e o controle retorna ao PARA;
Ao retornar o controle ao PARA, o valor de <Variável> é
comparado com o valor de <Fim>. Se os valores forem iguais,
os comandos são executados uma última vez e o controle
passa para o primeiro comando após o FIM-PARA. Se os
valores forem diferentes, os comandos são novamente
executados e o ciclo é repetido até que os valores se igualem.

Ao utilizar essa estrutura, tome os seguintes cuidados:


Se <Valor> for positivo, <Início> deve ser menor que <Fim>;
Se <Valor> for negativo, <Início> deve ser maior que <Fim>;
Deve-se ter certeza de que somando <Valor> a <Variável>
repetidas vezes, será atingido o valor de <Fim>.
Se esses cuidados não forem observados, provavelmente a
execução do algoritmo ficará presa num loop infinito.
Veja a sintaxe da estrutura de repetição com variável de controle:

Figura 3.7.

Agora, siga um exemplo para a comparação da utilização das três


estruturas de repetição vistas anteriormente. Primeiro, o problema:
calcular a média ponderada de 50 alunos, recebendo, para isso, três
notas com seus respectivos pesos. Assim, o primeiro passo da
resolução é:
CONT 1
LEIA P1, P2, P3
ENQUANTO CONT < = 50 FAÇA
LEIA NT1,NT2, NT3
MED (NT1*P1 + NT2*P2 + NT3*P3) / 10
MOSTRE MED
CONT := CONT + 1
FIM-ENQUANTO
Depois:
CONT 1
LEIA P1, P2, P3
REPITA
LEIA NT1,NT2, NT3
MED (NT1*P1 + NT2*P2 + NT3*P3) / 10
MOSTRE MED
CONT := CONT + 1
ATÉ QUE CONT > 50

Enfim:
LEIA P1, P2, P3
PARA J = 1 ATÉ 50 FAÇA
LEIA NT1,NT2, NT3
MED (NT1*P1 + NT2*P2 + NT3*P3) / 10
MOSTRE MED
FIMPARA

Observação: note que a condição da estrutura


REPITA, para funcionar, será exatamente o contrário
da condição da estrutura ENQUANTO.
A estrutura PARA não necessita da variável
acumuladora CONT, pois a própria estrutura se
incumbe de fazer a acumulação.
Comando ABANDONE
O comando ABANDONE é um comando de saída estruturada,
que permite o emprego dentro das estruturas de repetição
ENQUANTO, REPITA ou PARA.
Deve-se observar que seu uso estará sempre associado ao teste
de uma condição com a estrutura condicional SE.
Quando o comando ABANDONE é encontrado, o próximo
comando a ser executado é o primeiro comando logo após o fim do
comando de repetição interno, onde aparece o ABANDONE:
Veja a sintaxe desse comando:

Figura 3.8.

Exercício resolvido

Veja como elaborar um algoritmo que receba 200 números


inteiros aleatórios via teclado, calcule a soma dos números lidos e
mostre-a, no final. Se por acaso for lido um número negativo, o
programa deverá ser encerrado, dobrando o valor atual das somas
dos números lidos anteriormente:
ALGORITMO soma _ num
VAR
Num, soma : inteiro
CONT : inteiro
INÍCIO
CONT 1
ENQUANTO CONT <= 200 FAÇA
LEIA num
SE num < 0 ENTÃO
Soma soma * 2
ABANDONE
FIM-SE
Soma soma + num
CONT cont + 1
FIM-ENQUANTO
MOSTRE “a soma de todos os números é “, soma
FIM
Laços aninhados
Assim como era possível ter uma construção SE ENTÃO SENÃO
FIM-SE dentro de outra construção SE ENTÃO SENÃO FIM-SE,
também é possível ter um laço de repetição dentro do intervalo de
outro.
As regras de inclusão são similares, em ambos os casos. A
construção interna deve estar completamente embutida na
construção externa. Não pode haver sobreposição.
Não há limite para o número de estruturas que podem ser
colocadas dentro de uma estrutura qualquer, mas se o número for
muito grande, a compreensão e a visualização serão prejudicadas
pelo deslocamento excessivo para a direita.
Nos casos em que são necessárias muitas estruturas internas, a
solução é a utilização do processo de modularização do algoritmo,
assunto que será tratado nos próximos capítulos.
Uma regra importante que deve ser sempre seguida é que o início
e o final da estrutura mais interna devem ser sempre dentro da
estrutura imediata que a contém.
As figuras seguintes mostram exemplos de inclusões válidas e
inválidas, respectivamente:
Figura 3.9.

Exercício resolvido

Veja como elaborar um algoritmo que imprima a seguinte


seqüência:

1.1 1.2 1.3 1.4 1.5


1.6 1.7 1.8
2.1 2.2 2.3 2.4 2.5
2.6 2.7 2.8
3.1 3.2 3.3 3.4 3.5
3.6 3.7 3.8
4.1 4.2 4.3 4.4 4.5
4.6 4.7 4.8

Tabela 3.2.
ALGORITMO faz _ sequencia
VAR
I, j : inteiro
INÍCIO
PARA i = 1 ATÉ 4 FAÇA
PARA j = 1 ATÉ 8 FAÇA
MOSTRE i + “.” + j
FIM-PARA
FIM-PARA
FIM
Condições compostas a partir de
operadores lógicos
Para alguns problemas, as relações simples são inadequadas
para descrever as condições requeridas. O resultado poderia
usualmente ser obtido com inclusão, entretanto, isso pode tornar os
algoritmos desnecessariamente complicados e difíceis de entender.
Um método alternativo é utilizar condições compostas. Estas
condições são obtidas das relações simples, utilizando os
operadores lógicos E, OU e NÃO.
Veja os exemplos seguintes. Primeiro, sem uso de condição
composta:
.
.
.
SE salario < 400 ENTÃO
SE num _ faltas = 0 ENTÃO
salario salario + gratificacao
FIM-SE
FIM-SE

Agora, com o uso de condição composta:


.
.
.
SE salario < 400 E num _ faltas = 0 ENTÃO
salário salario + gratificacao
FIM-SE
Para treinar
1. Coloque o número que falta:

2. Paulo e Ernani possuem a mesma quantia de dinheiro. Paulo,


no entanto, tem mais dinheiro que Jorge e Jorge mais dinheiro
que Carlos. Um outro homem, Antônio, tem menos dinheiro que
Paulo e mais dinheiro que Carlos, mas não tem tanto quanto
Jorge. Ernani tem menos dinheiro que seu amigo José.
Se a diferença entre o dinheiro de cada um é de R$ 1250,00
e o menos rico tem R$ 5,00, quanto cada um possui?

3. Um fazendeiro morreu e deixou um rebanho de 142 vacas para


cinco filhos. No testamento, especificou:
para o primeiro filho, 1/3 do rebanho;
para o segundo filho, 1/4 do rebanho;
para o terceiro filho, 1/6 do rebanho;
para o quarto filho, 1/8 do rebanho;
e para o último filho, 1/9 do rebanho.

Os filhos verificaram que não podia ser dividido desse modo,


pois cada um receberia frações de vaca e nenhum queria abrir
mão de sua quota. Um vizinho inteligente resolveu o problema
satisfazendo a todos. Como isso aconteceu?

4. Um marinheiro cujo navio afundou viu-se só, em uma ilha, com


uma febre altíssima. Do naufrágio, sobraram duas garrafas de
medicamento. Numa delas, havia 3/4 do conteúdo de um
remédio e a outra, igual, estava completamente vazia. As
instruções para tomar o remédio prescreviam uma dose de
metade da garrafa, nem mais nem menos. Como obter
exatamente 1/2 garrafa?
Capítulo 4

Máximas de programação e
programação estruturada

A vida só pode ser compreendida olhando-se para trás, mas só


pode ser vivida olhando-se para frente!
Soren Kierkegaard
Verificação manual de algoritmos
De acordo com VILARIM (2004, p. 44-46), após a construção de
um algoritmo, existe uma forma “braçal” de verificar se ele está
realmente apresentando uma solução correta para o problema. Esta
forma, chamada método chinês (ou, ainda, teste de mesa), consiste
em simular manualmente a execução do algoritmo, comando após
comando, atentando para as entradas e saídas de dados, e,
principalmente, acompanhando o comportamento das variáveis
utilizadas.
“Fazer o chinês”, como é dito no jargão da área de computação,
equivale a colocar-se no papel do computador, anotando o que a
máquina faria ao encontrar cada comando.
Vamos ao primeiro exemplo, segundo VILARIM (2004). O
problema é calcular a média de dois números reais fornecidos pelo
usuário.
Rascunhando uma solução para o problema, podemos identificar:
Entradas: dois números reais;
Saída: a média dos números;
Cálculo necessário: somar os números e dividir o resultado por
dois.

A solução desse problema pode tomar como base o algoritmo


para somar dois números, acrescentando-se uma variável:
{Algoritmo para calcular a média de 2 números}
ALGORITMO calcmedia
VAR
num1, num2, soma, media: REAL
INÍCIO
LEIA NUM1, NUM2
SOMA NUM1 + NUM2
media - SOMA /2
MOSTRE media
FIM

Como saber se o algoritmo está calculando o valor correto?


Inicialmente, anotamos as variáveis do algoritmo, e ao lado de cada
uma, o valor armazenado (inicialmente indefinido):
num1 ?
num2 ?
soma ?
media ?

Na primeira leitura, suponhamos que o usuário forneça o valor 8.


Neste caso, as variáveis ficam com a seguinte situação na memória:
num1 8
num2 ?
soma ?
media ?

Na segunda leitura, suponhamos que o usuário forneça, agora, o


valor 7. As variáveis ficam da seguinte forma:
num1 8
num2 7
soma ?
media ?

Na seqüência, o computador calcula a soma dos números:


num1 8
num2 7
soma 15
media ?

E depois, calcula a média na atribuição seguinte:


num1 8
num2 7
soma 15
media 7.5

que será o valor impresso (media). De fato, a média dos números 8


e 7 é, mesmo, 7.5, logo, o algoritmo fez o cálculo corretamente.
Por uma questão de bom senso, podemos colocar valores de
entrada que facilitem os cálculos e que nos permitam antecipar a
resposta esperada (como uma prova real).
O estilo na programação
A importância do estilo

O estilo tem conseqüências importantes na programação de


computadores, algumas das quais serão vistas neste livro. Além da
questão de padrões profissionais, considerações de estilo podem
realmente levar a um aumento da qualidade dos programas, como,
por exemplo, a redução do número de erros cometidos no
desenvolvimento do programa. O próprio programa pode ser mais
facilmente lido e entendido por outros programadores que venham
efetuar modificações. A manutenção de programas existentes é o
ponto central das constantes necessidades de mudança e consome
grande parte do trabalho do programador profissional.
Um bom estilo de programação é um fator importante no sucesso
do programador e na obtenção de resultados apreciáveis.

A qualidade do programa

Podemos dizer que um bom programa é aquele que possua as


seguintes características.

O programa funciona
Nunca devemos nos esquecer de que a característica mais
importante de um programa é sua funcionalidade. Isso parece óbvio,
mas é difícil averiguar esse quesito em programas de tamanho
significativo ou de grande complexidade.
Os programadores devem tomar muito cuidado para que o
programa eventualmente implementado seja realmente o requerido.
A solução parcial de um problema leva a insatisfação do usuário. É
importante que as especificações (que podem mudar) sejam
revistas continuamente, desde o projeto até a implementação.

O programa não tem erros


Muitos programadores aceitam erros no programa como uma
conseqüência natural da condição humana e vêem a operação de
correção de erros como um fato corriqueiro. Não há razão para que
tal ocorra. Em geral, os programadores são responsáveis pelos
erros dos programas. Eles não fazem isso deliberadamente
(ninguém gosta de corrigir programas) e, realmente, podem evitar a
maioria destes erros.

O programa é bem documentado


É importante que um programa seja bem documentado. A
documentação existe para auxiliar o entendimento ou o uso de um
programa. Isso é importante para os programadores encarregados
da manutenção dos programas, sendo também de grande valor para
o próprio programador.
A documentação pode se dar de duas formas: documentação
externa, que inclui manuais de referência, descrição de algoritmos
etc., e documentação interna, incluída no programa
(essencialmente, os comentários). A documentação interna é de
maior interesse do programador, enquanto a documentação externa
é dirigida aos usuários do programa, que não precisam ou não
desejam conhecer detalhes da própria codificação, mas precisam
saber o que o programa faz e como ele funciona. A documentação
externa fornece uma importante descrição complementar do
programa.

O programa é eficiente
Esta característica está relacionada com o desempenho do
programa. Programas mais rápidos e que consomem menos espaço
de memória são considerados mais eficientes.
Outro fator importante para avaliarmos a eficiência de um
programa é o funcionamento correto e confiável, isto é, a garantia
de que todas as suas especificações tenham sido obtidas e de que
erros não previstos sejam improváveis de ocorrer. Para que um
programa tenha tais características, é necessário que ele passe por
um processo contínuo de manutenção e modificação para
acompanhar as mudanças de especificações e tecnologia.
A programação como atividade
humana
Um programador de sucesso deve aprender a dominar uma
grande variedade de habilidades, desde aquelas de natureza
criativa, tais como análise e projeto da solução de problemas, até as
tarefas puramente mecânicas, como codificação. Essas tarefas
requerem diferentes habilidades e todas devem ser executadas
corretamente, caso um programa de boa qualidade esteja para ser
produzido.
Programadores não são máquinas: são seres humanos
executando tarefas complexas e, por esta razão, pode-se esperar
que cometam erros, como resultado de sua condição humana.
Seres humanos têm limitações reais quanto à capacidade de
percepção e desempenho, que varia de indivíduo para indivíduo. É
importante que cada programador reconheça e aprenda a viver
dentro de suas limitações.
A seguir, vou retratar os efeitos de vários fatores que afetam o
trabalho de um programador.

Efeitos do processamento da informação

Erros devidos aos efeitos do processamento resultam das


limitações inerentes a falhas de percepção, da memória e do
conhecimento humano. Inegavelmente, a programação tem um
grande componente de processamento da informação, que
compreende itens como especificação e requisitos dos problemas,
regras e características da linguagem de programação que está
sendo utilizada, entre outros. Sem entrar em muitos detalhes, vemos
que o programador, como ser humano, é um processador de
informações imperfeito: símbolos são confundidos, detalhes de
linguagem são esquecidos, execuções de rotinas são mal
interpretadas, e assim por diante.
Efeitos sociais

Muitos aspectos de programação envolvem comunicação entre


diferentes pessoas (direta ou indiretamente, por meio da divisão da
codificação do programa). Em grandes projetos de programação, os
programadores raramente trabalham numa base estritamente
individual. O grupo pode se beneficiar da cooperação entre os
membros ou ser prejudicado pela competição excessiva. Aqui, há
claramente uma ligação com os efeitos de personalidade. Os
programadores devem ser capazes de aceitar sugestões e críticas
feitas; erros não devem ser interpretados como advertências
públicas de deficiências.
Máximas de programação
“Máximas de programação” são regras práticas para a elaboração
de algoritmos com qualidade. Alguns exemplos são apresentados a
seguir:
Algoritmos devem ser feitos para serem lidos por seres
humanos: tenha em mente que seus algoritmos deverão ser
lidos e entendidos por outras pessoas (e por você mesmo), de
tal forma que possam ser corrigidos, modificados, ou receber
manutenção;
Escreva os comentários no momento em que estiver
escrevendo o algoritmo: um algoritmo não documentado é um
dos piores erros que um programador pode cometer; é sinal de
amadorismo. Como o objetivo da escrita de comentários é
facilitar o entendimento do algoritmo, eles devem ser tão bem
concebidos quanto o próprio algoritmo, e a melhor maneira de
conseguir isso é escrevê-los nos momentos de maior intimidade
com os detalhes, ou seja, durante a resolução do problema.
Lembre-se: é melhor ter um algoritmo sem comentários do que
ter um algoritmo com comentários desatualizados;
Os comentários deverão acrescentar alguma coisa às pessoas
que farão manutenção em seu algoritmo: o conjunto de
comando nos diz o que está sendo feito; os comentários
deverão nos dizer o porquê;
Use comentários no cabeçalho do algoritmo: todo algoritmo ou
procedimento deverá ter comentários em seu início para
explicar o que ele faz e fornecer instruções para seu uso.
Alguns destes comentários seriam:
Uma descrição do que faz o algoritmo;
Como utilizá-lo;
Explicação do significado das variáveis mais importantes;
Estruturas de dados utilizadas;
Data de escrita;
Autor;
Nomes de quaisquer métodos especiais utilizados, com
referências nas quais mais informações possam ser
encontradas.

Utilize espaços em branco para melhorar a legibilidade:


espaços em branco, inclusive linhas em branco, são muito
valiosos, pois melhoram a aparência de um algoritmo. Veja:
Deixar uma linha em branco entre as declarações e o corpo
do algoritmo;
Deixar uma linha em branco antes e outra depois de um
comentário;
Separar grupos de comandos que executam funções
lógicas distintas por uma ou mais linhas em branco;
Utilizar brancos para indicar precedência de operadores.
Em vez de “A + B * C” é bem mais agradável a forma “A +
B*C”.

Escolha nomes representativos para suas variáveis: os nomes


das variáveis deverão identificar o melhor possível as
funcionalidades que elas representam. Por exemplo, X Y + Z
é muito menos claro que PREÇO CUSTO + LUCRO. Uma
seleção adequada de nomes de variáveis é o princípio mais
importante da legibilidade de algoritmos;
Um comando por linha é suficiente: a utilização de vários
comandos por linha é prejudicial, por várias razões, entre as
quais se destacam:
O algoritmo pode ficar ilegível;
O algoritmo fica mais difícil de ser depurado.

Veja um exemplo:
A 14, 2 ;I 1; ENQUANTO I<10 FAÇA X X + 1 ; K I*K;I I+1
FIM-ENQUANTO;

Agora, com cada comando em uma linha:


A 14,2
I 1
ENQUANTO I<10 FAÇA
X X +1
K I * K
I I +1
FIM-ENQUANTO;

Caso desejássemos incluir um novo comando dentro de


ENQUANTO, seria necessário reescrever toda a linha.
Utilize parênteses para aumentar a legibilidade e prevenir-se
contra erros;

Veja uns exemplos, primeiro, com poucos parênteses:


A *B *C / (D *E *F)
A *B / C *D / E *F
A **B **C
A / B / C / D
X > Y ou Q
A + B < C

Agora, com parênteses extras:


(A *B *C) / (D *E *F)
((((A * B) / (C) * D) / E) * F
(A ** B) **C
((A / B) /C) / D
(X > Y) ou Q
(A + B) < C

Utilize “identação” para mostrar a estrutura lógica do algoritmo:


a identação não deve ser feita de forma caótica, mas segundo
certos padrões estabelecidos;
Lembre-se: sempre que for feita uma modificação no algoritmo,
os comentários associados devem ser alterados, e não apenas
os comandos. Antes não comentar do que deixar um
comentário errado.

Para concluir, disponibilizo, como exemplo, um trecho de um


algoritmo mal escrito. A análise das regras de programação violadas
será apresentada a seguir:
INÍCIO

INTEIRO: XPT, I, II, III, IIIII


LEIA (XPT, IIIII)
I 1
ENQUANTO I < XPT FAÇA
SE I < IIIII ENTÃO SE IIIII = 20 ENTÃO II XPT + 2
SENÃO III IIIII ^ XPT; SENÃO III XPT
IIIII III + I; FIM-SE; FIM-SE {sem comentários}
I I + 1
FIM-ENQUANTO; IMPRIMA (I, II, IIIII, III, XPT); FIM.

Esse código fere todas as regras pregadas pelas máximas de


programação, sendo que as mais graves são: falta de identação,
nomes não significativos para as variáveis e ausência de
comentários. Sugerimos reescrever o algoritmo de forma a melhorar
sua qualidade.
Algoritmos estruturados
Computadores são máquinas destinadas a resolver problemas
com grande rapidez. Uma operação aritmética pode ser efetuada
num tempo da ordem de um microssegundo, isto é, um milionésimo
de segundo; computadores mais potentes estão atingindo o tempo
de um nanossegundo, ou um milésimo de microssegundo. O
aproveitamento dessa grande rapidez exige que as operações
sejam efetuadas automaticamente, sem interferência humana.
Qualquer decisão humana demanda dezenas de segundos (às
vezes, horas ou dias). Pouco adiantaria a máquina fazer uma
operação em 1/1.000.000 de segundo, se em seguida ela tivesse de
esperar dezenas de segundos (ou horas, ou dias) para efetuar a
próxima operação. Em casos como esses, seria preferível usar
calculadoras.
Por outro lado, máquinas dotadas de rapidez e automatismo são
muito caras. Investimentos nesse nível só se justificam se elas
puderem ser usadas para resolver problemas das mais diversas
naturezas: se elas forem flexíveis.
A flexibilidade exige que, para cada problema a ser levado ao
computador, sejam planejadas as operações correspondentes. O
automatismo, por outro lado, exige que o planejamento dessas
operações seja feito previamente, antes de utilizar o computador.
Então, a utilização de um computador para resolver problemas
exige, antes de mais nada, que se desenvolva um algoritmo, isto é,
que se faça a descrição de um conjunto de comandos que,
obedecidos, provocarão uma sucessão finita de ações que
resultarão na resolução do problema proposto, como já vimos nos
capítulos anteriores. Esse algoritmo tem de ser transmitido ao
computador e armazenado em sua memória, para, em seguida, ser
posto em execução e conduzir o computador à solução desejada. O
algoritmo deve, portanto, prever antecipadamente todas as
situações que possam ocorrer quando for posto em execução.
Se um problema for simples e pequeno, não há maiores
dificuldades em desenvolver um algoritmo adequado para resolvê-
lo, nem há necessidade de aplicação de técnicas especiais. Porém,
hoje em dia, os problemas levados aos computadores são de porte
cada vez maior, mais complexos. Os algoritmos para resolvê-los
ainda devem ser desenvolvidos pelo ser humano, mas podem
ultrapassar os limites de sua compreensão.
Por esta razão, nas últimas décadas surgiram técnicas que
permitem sistematizar e ajudar o desenvolvimento de algoritmos
para a resolução de grandes e complexos problemas nos
computadores, as chamadas técnicas de desenvolvimento
estruturado de algoritmos.
Os objetivos destas técnicas são:
Facilitar o desenvolvimento dos algoritmos;
Facilitar o desenvolvimento pelos seres humanos;
Antecipar a comprovação de sua correção;
Facilitar sua manutenção e sua modificação;
Permitir que seu desenvolvimento possa ser empreendido
simultaneamente por uma equipe de pessoas.

Para atingir esses objetivos, o desenvolvimento estruturado


preconiza que:
Os algoritmos sejam desenvolvidos por refinamentos
sucessivos, partindo de uma descrição geral e, gradativa e
sucessivamente, atacando as minúcias e particularidades. Este
desenvolvimento também se denomina “construção hierárquica
de algoritmos” e “desenvolvimento de cima para baixo” (em
inglês, top-down);
Os sucessivos refinamentos são módulos, que delimitam
poucas funções e são o mais independentes possível, isto é,
conservam poucos vínculos com outros módulos;
Nos módulos deve ser usado um número limitado de diferentes
comandos e de diferentes estruturas de controle.

A técnica do desenvolvimento estruturado de algoritmos, por si só,


não atinge automaticamente os objetivos visados. Ela apenas
defende um procedimento sistemático que nos ajuda a atingir esses
objetivos. É necessário, ainda, por parte de quem a emprega,
esforço e disciplina na busca incessante da simplicidade e da
clareza.
O refinamento sucessivo dos algoritmos permite uma abordagem
mais segura e objetiva do problema. A integração dos módulos é
mais acabada, pois cada módulo é atacado quando já se estudou
claramente o ambiente em que ele deve atuar. A alteração dos
módulos já desenvolvidos é, teoricamente, desnecessária. Na
prática, consegue-se diminuir muito essas alterações pela atenção e
disciplina. Os módulos de mais alto nível podem ser testados antes
de se desenvolverem os de níveis mais baixos, permitindo o
desenvolvimento mais seguro dos algoritmos.
A divisão em módulos funcionais nos permite contornar a
limitação humana e compreender a complexidade de um projeto.
Cada módulo pode ser desenvolvido ou analisado de forma quase
independente, em virtude dos poucos vínculos que deve manter
com outros módulos do algoritmo. Cada módulo pode, portanto, ser
desenvolvido por uma pessoa, após definir poucos acordos com o
resto da equipe.
O uso limitado de diferentes comandos e de diferentes estruturas
de controle pode restringir bastante o raciocínio, que fica obrigado a
se enquadrar em poucas formas. Porém, os benefícios, em termos
de simplicidade e clareza do produto final, são imensos.
A clareza e a simplicidade de um algoritmo são atributos
inestimáveis quando se faz sua manutenção e sua modificação
(para atender a novas aplicações).
Para treinar
1. Alguns bárbaros aprisionaram um sábio e propuseram-lhe o
seguinte:

“Se você disser uma verdade, será queimado, e se disser


uma mentira, será afogado. De que maneira prefere morrer?”
O sábio deu uma resposta tal que foram obrigados a libertá-
lo. Qual foi a resposta?

2. Um elevador sai do térreo com uma pessoa. No andar seguinte,


entram duas pessoas. No outro, entra uma pessoa e saem
duas. No próximo, saem duas e entra uma. No seguinte, entram
três e, no último, sai uma pessoa. Quantos andares o elevador
subiu? Se a carga máxima do elevador é seis pessoas, em qual
andar houve perigo?

3. Um sultão tem dez sacos, contendo 100 moedas de ouro cada


um. Dois sacos contêm somente moedas falsas. A moeda
verdadeira tem massa 10 g e a moeda falsa de 11 g. Usando
uma balança calibrada em gramas e em uma só aferição, como
o sultão pode descobrir o saco de moedas falsas?

4. Depois de passar N dias em férias, um estudante observou que:


Choveu sete vezes; de manhã ou à tarde;
Quando chove de manhã, não chove à tarde;
Houve cinco tardes sem chuva;
Houve seis manhãs sem chuva.

Desse modo, concluímos que o valor de N é –– dias.


Capítulo 5

Estrutura de dados simples:


vetores, matrizes e registros

Nossas dúvidas são traidoras e nos fazem perder o que, com


frequência, poderiamos ganhar, por simples medo de arriscar.
William Shakespeare
Vetores (estrutura de dados
homogêneos)
Até agora, trabalhamos somente com variáveis simples, que nos
permitem armazenar um único valor a cada momento.
Muitas vezes, porém, precisamos usar um grupo de variáveis do
mesmo tipo, mas que possam ser referenciadas pelo conjunto ou,
individualmente, com o mesmo nome.
Um exemplo dessa necessidade pode ser visto a seguir, em um
pseudocódigo que soma cinco números e, depois de mostrar o
resultado, imprime os números lidos na ordem inversa da leitura.

Primeiro exemplo
ALGORITMO SOMA _ 5 _ NUMEROS
VAR
soma, num1, num2, num3, num4, num5 : REAL
INICIO
LEIA num1, num2, num3, num4, num5
SOMA num1+num2+num3+num4+num5
MOSTRE soma
MOSTRE num5, num4, num3, num2, num1 FIM
FIM ALGORITMO

Imagine, agora, se em vez de cinco números fossem 100!


O programa ficaria com a escrita imensa, além de ocupar um
espaço de memória da máquina muito grande. Somente a
declaração das variáveis tornaria impraticável a redação do
algoritmo.
Um vetor, então, seria considerado uma variável de memória
indexada, ou com índices, um agregado homogêneo de dados, ou
seja, um grupo de variáveis do mesmo tipo que recebem o mesmo
nome e são individualizadas por um índice.
A sintaxe utilizada para declararmos um vetor em um
pseudocódigo é:
VAR
Nomedovetor : VETOR[LI : LS] DE tipo _ de _ dado
Em que:
LI é o limite inferior;
LS é o limite superior;
Ambos só podem ser valores inteiros.

O número de elementos de um vetor será dado por LS - LI + 1.


Isso significa que as posições do vetor são identificadas a partir de
LI, com incrementos unitários, até LS:

Figura 5.1.

Podemos, agora, reescrever o algoritmo do primeiro exemplo,


utilizando o recurso de um vetor:
ALGORITMO SOMA _ 5 _ NUMEROS
VAR
vetnum : VETOR[1 : 5] DE REAL
ind : INTEIRO
soma : REAL
INÍCIO
Soma 0
PARA IND DE 1 ATE 5 FAÇA
LEIA vetnum[IND]
Soma soma+vetnum[ind]
FIM-PARA

MOSTRE soma

PARA IND DE 5 ATE 1 PASSO - 1 FAÇA


MOSTRE vetnum[IND]
FIM-PARA
FIM
FIM ALGORITMO

Segundo exemplo
Qual será o valor da variável RESULT impressa pelo algoritmo
seguinte?
ALGORITMO EXEMPLO2
VAR
RESULT : REAL
vetexemplo : VETOR[1 : 5] DE REAL
I : INTEIRO
INÍCIO
vetexemplo[1] 2
vetexemplo[2] 4
vetexemplo[3] 1
vetexemplo[4] 3
vetexemplo[5] 5
RESULT vetexemplo[1] + vetexemplo[5]
MOSTRE RESULT
RESULT vetexemplo[2] - vetexemplo[5]
MOSTRE RESULT
RESULT vetexemplo[4] * vetexemplo[1] - result
MOSTRE RESULT
I 3
RESULT vetexemplo[i]
MOSTRE RESULT
RESULT vetexemplo[i] / vetexemplo[vetexemplo[1]]
MOSTRE RESULT
FIM
FIM ALGORITMO

A solução seria esta. Observe:

Figura 5.2.

Agora, veja o que será impresso pelo algoritmo:


RESULT VETEXEMPLO[1] + VETEXEMPLO[5]
RESULT 2 + 5

Aqui, foi impresso 7.


Agora, será impresso -1:
RESULT VETEXEMPLO[2] - VETEXEMPLO[5]
RESULT 4 - 5

Depois, 7:
RESULT VETEXEMPLO[4] * VETEXEMPLO[1] - result
RESULT 3 * 2 - (-1)

Agora, 1:
I 3
RESULT VETEXEMPLO[I]
RESULT VETEXEMPLO[3]

E, enfim, 0,25:
RESULT VETEXEMPLO[I] / VETEXEMPLO[VETEXEMPLO[1]]
RESULT VETEXEMPLO[3] / VETEXEMPLO[2]
RESULT 1 / 4

Terceiro exemplo

Dado o vetor VETMENSAGEM iniciado com os seguintes valores:

Figura 5.3.

É preciso mostrar sua configuração depois de executado o


algoritmo seguinte:
ALGORITMO EXEMPL03
VAR
Aux : TEXTO
VETMENSAGEM: VETOR[1 : 8] DE TEXTO
I : INTEIRO
INÍCIO
VETMENSAGEM [1] “!”
VETMENSAGEM [2] “U”
VETMENSAGEM [3] “O”
VETMENSAGEM [4] “T”
VETMENSAGEM [5] “R”
VETMENSAGEM [6] “E”
VETMENSAGEM [7] “C”
VETMENSAGEM [8] “A”
PARA I DE 2 ATÉ 4 FAÇA
Aux VETMENSAGEM [I]
VETMENSAGEM [I] VETMENSAGEM [8 - I +1]
VETMENSAGEM [8 - I +1] Aux
FIM-PARA
Aux VETMENSAGEM [1]
VETMENSAGEM [1] VETMENSAGEM [8]
VETMENSAGEM [8] aux
FIM
FIM ALGORITMO

Observe a solução:

Figura 5.4.

Exercício resolvido

Vamos construir um algoritmo que preencha um vetor de 500


posições numéricas e, no final da leitura, somar somente os valores
da sua posição par:
ALGORITMO Soma _ vetor
VAR
VetValores : vetor [1 : 500] de inteiro
Soma, j : inteiro
INÍCIO
PARA j = 1 ATÉ 500 FAÇA
LEIA vetvalores[j]
FIM-PARA

PARA j = 2 ATÉ 500 PASSO 2 FAÇA


Soma soma + vetvalores[j]
FIM-PARA
MOSTRE “A soma das posições pares foi “, soma
FIM
Matriz (estrutura de dados
homogênea)
No item anterior, podemos verificar a utilidade do vetor na solução
de diversos problemas; estes envolviam arranjos homogêneos
unidimensionais, por isso o emprego do vetor.
No entanto, podemos encontrar diversos problemas que
envolvam, em sua solução, arranjos homogêneos
multidimensionais, chamados matrizes, que, na verdade, são
simplesmente vetores de múltiplas dimensões, sendo mais
utilizados os de duas e três dimensões.
A sintaxe utilizada para declararmos uma matriz em um
pseudocódigo é:
VAR

NomedaMatriz : MATRIZ [Li1 : Ls1, Li2 : Ls2, Lin : Lsn]


de Tipodedado

Em que:
Li é o limite inferior;
Ls é o limite superior;
Li e Ls só podem ser valores inteiros;
1, 2 e n são os índices da matriz.

O número de dimensões da matriz será igual ao número de


vírgulas (,) da declaração + 1.
O número de elementos será igual ao produto do número de
elementos de cada dimensão:
(Ls1 - Li1 + 1) * (Ls2 - Li2 + 1) * ........ * (Lsn - Lin + 1)

Primeiro exemplo

Vamos descobrir o número de elementos e de dimensões das


matrizes especificadas:
Mat1 : MATRIZ [0 : 2 , 1 : 5] DE REAL
e
Mat2 : MATRIZ [1 : 3 , 2 : 4 , 3 : 4] DE TEXTO

Veja a solução:
Mat1 tem duas dimensões e 15 elementos: (2 - 0 + 1) * (5 - 1 +
1);
Mat2 tem três dimensões e 18 elementos: (3 - 1 + 1) * (4 - 2 +
1) * (4 - 3 + 1).

Segundo exemplo

Observe a matriz:
cartao _ loteria : MATRIZ[ 1..13, 1..3] DE TEXTO

Essa matriz do tipo texto possui duas dimensões, uma de 13


posições e outra de 3 posições (13 x 3), e pode ser utilizada para
armazenar, por exemplo, os palpites de um jogador de loteria
esportiva. Para cada um dos trezes jogos, podemos marcar um X no
respectivo palpite, de acordo com as posições da segunda
dimensão, sendo uma para o primeiro time ter ganhado, duas para
empate e três como palpite para o segundo time ter ganhado o jogo.
Assim sendo, o carta _ loteria[6,2] que contenha um X indica que o
jogador marcou para o sexto jogo do concurso a coluna do meio.

Veja o exemplo da matriz (cartão _ loteria):

Tabela 5.1.

Vamos fazer uma rápida comparação. Se tivéssemos de resolver


este mesmo problema com um vetor, teríamos de possuir um
controle muito mais complicado, para entender e aplicar, pois, de
cara, teríamos de criar três vetores de 13 posições que simulariam
as colunas 1, 2 ou 3. A posição de cada um destes três vetores que
ficasse vazia significaria que o jogador poderia ter preenchido uma
das outras duas posições. Veja como ficaria a matriz cartão _ loteria
como um vetor:

Tabela 5.2.

Tabela 5.3.

Tabela 5.4.

Voltemos ao caso das matrizes. Para fazer referência a um


elemento de uma matriz são necessários tantos índices quantas são
suas dimensões. Em uma matriz bidimensional (duas dimensões), o
primeiro índice indica a linha, e o segundo, a coluna. Para matrizes
com três dimensões, repete-se a estrutura bidimensional tantas
vezes quantos são os elementos da terceira dimensão, numerando-
os de acordo com os limites especificados em sua declaração de
tipo.
As linguagens de programação, em geral, limitam a dimensão de
uma matriz em um máximo de três. Mas será que existe
necessidade de mais de três índices para identificar uma variável?
Vale, ainda, ressaltar que, para trabalharmos com uma matriz como
leitura ou impressão de seus elementos, devemos utilizar um
aninhamento da estrutura de repetição com variável de controle
quantas forem o número de índices da matriz. Veja um exemplo:
ALGORITMO ExemploMatriz
VAR
MatCodigo : MATRIZ[1 : 3 , 1 : 2] DE INTEIRO
I , J : INTEIRO
INÍCIO
MatCodigo [1,1] 1
MatCodigo [1,2] 2
MatCodigo [2,1] 3
MatCodigo [2,2] 4
MatCodigo [3,1] 5
MatCodigo [3,2] 6
PARA I DE 1 ATÉ 2 FAÇA
PARA J DE 1 ATÉ 3 FAÇA
MOSTRE MatCodigo[J,I]
FIM-PARA
FIM-PARA
FIM
FIM-ALGORITMO

O algoritmo mostrará os seguintes valores:


1
3
5
2
4
6
Registros (estrutura de dados
heterogênea)
Com o modelo de dados registro, será possível, a exemplo dos
vetores e matrizes, lidar com conjuntos de dados como um arranjo
único ou pelo acesso a um item individual. A diferença básica dos
modelos de vetor e matriz para o modelo de registro é, como será
visto, o fato de cada componente poder ser de um tipo de dado
diferente. Sua sintaxe é a seguinte:
TIPO NomedoTipo = REGISTRO
Campo1 : TIPODEDADO1
Campo2 : TIPODEDADO2
CampoN : TIPODEDADON
FIM-REGISTRO

Veja um exemplo:
TIPO r = REGISTRO
Nome,Sexo : TEXTO
Salario : REAL
Idade : INTEIRO
FIM-REGISTRO
r : REG

A especificação corresponde à definição de um modelo (r) de um


registro (aglomerado heterogêneo de dados) e a criação de uma
área de memória denominada REG, capaz de conter quatro
subdivisões de tipos diferentes.
A atribuição de valores às variáveis que compõem o registro pode
ser qualificada da seguinte maneira:
REG.Nome “Fulano de Tal”
REG.Sexo “M”
REG.Salario 450,00
REG.Idade 23

Quando no algoritmo não houver ambigüidades, isto é, não


existirem outras variáveis com o mesmo nome das variáveis do
registro, a qualificação é desnecessária. Em nosso exemplo,
teríamos:
Nome “Fulano de Tal”
Sexo “M”
Salario 450,00
Idade 23

Vale a pena observar que uma estrutura do tipo registro pode


conter outra estrutura de registro:
TIPO r = REGISTRO
Matricula : INTEIRO
Nome : TEXTO
Endereco : ENDER
FIM-REGISTRO
r : REGALUNO

O tipo de dado definido como ENDER não é básico. Portanto, ele


precisa ser definido como segue:
TIPO ENDER = REGISTRO
Rua,Bairro,Cidade : TEXTO
Nro : INTEIRO
FIM-REGISTRO

Isso significa que ENDER é o nome do modelo de um outro


registro embutido no modelo (r). Um exemplo de atribuição de
valores às variáveis desse registro seria:
REGALUNO.Matricula 15255
REGALUNO.Nome “Ciclano da Silva Gomes”
REGALUNO.ENDERECO.Rua “Dom Pedro I”
REGALUNO.ENDERECO.Bairro “Ipiranga”
REGALUNO.ENDERECO.Cidade “São Paulo”
REGALUNO.ENDERECO.Nro 155

Observe que a referência aos campos de registros torna cansativa


a escrita do código, pois deixa muito longo o nome da variável.
Utilizando o último exemplo, teríamos:
COM REGALUNO FAÇA
Matricula 15255
Nome “Ciclano da Silva Gomes”
ENDERECO.Rua “Dom Pedro I”
ENDERECO.Bairro “Ipiranga”
ENDERECO.Cidade “São Paulo”
ENDERECO.Nro 155
FIM
Para treinar
1. Nenhum homem é bom, mas alguns não são maus. Logo:
Todos os homens não são maus;
Nenhum homem é mau;
Todos os homens não são bons.

2. Três pessoas, num bar, fizeram uma despesa que custou R$


9,00 para cada uma, totalizando R$ 27,00. Porém, cada pessoa
deu ao garçom R$ 10,00. Por falta de troco, este devolveu R$
5,00. Destes, tiraram-se R$ 3,00, que lhe deram como gorjeta.
Então, como sobraram R$ 2,00?

3. Dez viajantes hospedaram-se num hotel e pediram um quarto


para cada um. O hoteleiro, que só dispunha de nove quartos,
fez o seguinte: no quarto número um, colocou dois viajantes; no
número dois, o terceiro viajante; no número três, o quatro; no
número quatro, o quinto; no número cinco, o sexto; no número
seis, o sétimo; no número sete, o oitavo; no número oito, o
nono, e no número nove, foi buscar um dos que ficaram no
quarto número um, resolvendo, assim, a situação. Como foi
possível?

4. Um jovem mora perto da estação do metrô de Manhattan. Ele


tem duas namoradas, uma no Brooklyn e outra no Bronx.
Para visitar a garota do Brooklyn, ele toma o metrô na
plataforma do lado do centro comercial. Para visitar a garota do
Bronx, ele toma o metrô na plataforma do lado residencial.
Como gosta igualmente das duas namoradas, ele simplesmente
toma o primeiro trem que chega. Assim, ele deixa o destino
escolher se deve ir ao Bronx ou ao Brooklyn. Todo sábado à
tarde ele chega à estação.
Tanto o trem do Bronx como o do Brooklyn irrompem na gare
ferroviária de dez em dez minutos. Entretanto, alguma razão
desconhecida faz que nosso amigo passe mais tempo com a
garota do Brooklyn! Na verdade, em cada dez visitas, nove são
feitas a esta garota; com quem afinal ele vai acabar casando.
Você sabe dizer por que o destino favoreceu tanto a garota
de Brooklyn?
Capítulo 6

Classificação e pesquisa de
elementos de um vetor

O importante não é vencer todos os dias, mas lutar sempre.

W. Martins

Em computação, classificação é o processo de rearranjar dado


conjunto de objetos em uma ordem específica. O propósito da
classificação é facilitar a busca futura de elementos no conjunto
classificado.
Existem vários métodos de classificação interna (estruturas na
memória principal) e externas (arquivos). A escolha do método de
classificação interna leva em consideração um compromisso entre
eficiência do método e esforço de implementação.
Classificação de elementos de um
vetor: “método da bolha”
Como classificar um vetor numérico de 20 elementos em ordem
crescente?
Para realizar essa tarefa, vamos tomar como exemplo o vetor
VETCODIGO, com 20 elementos. A idéia é comparar os elementos
dois a dois e “jogar” os elementos mais altos (maiores) para as
últimas posições do vetor, até obter o vetor classificado. Uma vez
que o elemento mais alto atingir a última posição, é reduzido o
tamanho do vetor a ser classificado. Esse método de classificação é
conhecido como “bolha” e não é o mais eficiente, mas é um dos
mais conhecidos, pela sua simplicidade e facilidade de
compreensão:
ALGORITMO CLASSIF _ VETOR _ ORDEM _ CRESCENTE
VAR
VETCODIGO : VETOR[1..20] DE INTEIRO
I, J : INTEIRO
AUX : INTEIRO
INÍCIO

{carregamento dos elementos no vetor}

PARA I = 1 ATÉ 20 FAÇA


LEIA Vetcodigo [I]
FIM-PARA

{ordenação do vetor}

J 20

ENQUANTO J > 1 FAÇA


PARA I = 1 ATÉ J - 1 FAÇA
ATUAL VETCODIGO[I]
SEGUINTE VETCODIGO[I + 1]
SE ATUAL > SEGUINTE ENTÃO
{troca os elementos}

AUX VETCODIGO[I]
VETCODIGO[I] VETCODIGO[I + 1]
VETCODIGO[I + 1] AUX
FIM SE
FIM-PARA

J J - 1
FIM-ENQUANTO
{impressão dos elementos ordenados}

PARA I = 1 ATÉ 20 FAÇA

MOSTRE VETCODIGO[I]

FIM-PARA

FIM
FIM ALGORITMO
Pesquisa seqüencial de um
elemento em um vetor
A pesquisa seqüencial, ou linear, é o método mais direto de
encontrar um elemento particular em um vetor não classificado. Esta
técnica envolve a verificação seqüencial de cada elemento do vetor
até que o elemento desejado seja encontrado (neste caso, diz-se
que a pesquisa foi bem sucedida) ou até que todos os elementos do
vetor tenham sido verificados, mas o elemento desejado não seja
encontrado (pesquisa malsucedida).
Como exemplo, suponha uma situação em que seja preciso
construir um algoritmo que leia um nome e verifique se este nome
encontra-se em um vetor com outros 50 nomes. O algoritmo deve
usar a pesquisa seqüencial. Veja:
ALGORITMO Pesquisa _ nome
VAR
CHAVE: TEXTO
VETNOMES : VETOR[1 : 50] DE TEXTO
I: INTEIRO
ACHOU: LÓGICO

INÍCIO
{carregamento dedados no vetor}

PARA I = 1 ATÉ 50 FAÇA


LEIA VETNOMES[I]
FIM-PARA

{pesquisa do valor fornecido}

LEIA CHAVE
I 1

ACHOU FALSO
ENQUANTO I <= 50 E NÃO ACHOU FAÇA
SE VETNOMES[I] = VALOR ENTÃO
ACHOU VERDADEIRO
SENÃO
I I + 1
FIM-SE
FIM-ENQUANTO
{resultado da pesquisa}
SE ACHOU ENTÃO
MOSTRE CHAVE, “ FOI ENCONTRADO ! ”
SENÃO
MOSTRE CHAVE, “ NÃO FOI ENCONTRADO ! ”
FIM-SE

FIM
FIM ALGORITMO
Pesquisa binária de um elemento
em um vetor
Se os elementos de um vetor forem previamente classificados,
pesquisas muito mais eficientes poderão ser conduzidas. Uma
técnica simples de pesquisa em um vetor carregado (seus
elementos têm valores) e classificado (estejam em ordem
ascendente ou decrescente) é conhecida como método de pesquisa
binária.
A pesquisa binária pode ser feita sobre o vetor classificado a fim
de verificar se determinado valor externo existe ou não no vetor. O
meio aproximado do vetor é localizado e seu valor examinado. Se
este valor é mais alto que o valor procurado, outro elemento do meio
da primeira metade é examinado e o procedimento é repetido na
primeira metade, até que o valor requerido seja ou não encontrado.
Se o valor for mais baixo que o procurado, um elemento médio da
segunda metade é tentado e o procedimento se repete. Este
procedimento continua até que o elemento desejado seja
encontrado ou o intervalo de pesquisa se torne vazio.
Este algoritmo serve-se de um conceito análogo ao conceito de
“tamanho lógico”, usado em alguns algoritmos de ordenação. O
“tamanho lógico” indica o número de elementos de um vetor, ou
seja, o vetor logicamente existe desde o elemento um até o
elemento N, sendo que N é o “tamanho lógico”. Na pesquisa binária,
são utilizados o “início lógico” e o “final lógico” do vetor, ou seja,
posições do vetor em que começam e terminam os dados
considerados (vetor lógico). O meio do vetor lógico, no qual está o
elemento a ser comparado, em geral é calculado pela adição do
“início lógico” e do “final lógico”, dividido por dois, desconsiderando
as casas decimais.
Vejamos um exemplo. Nele, vamos assumir que o vetor já foi lido
e está ordenado:
ALGORITMO Pesquisa _ binaria
VAR
VETMATRIC : VETOR[1 : 50] DE INTEIRO
valor, baixo, alto, medio : INTEIRO
achou: LÓGICO

INÍCIO
MOSTRE “Entre com o valor desejado para pesquisa”
LEIA valor
achou FALSO
baixo 1
alto 50
ENQUANTO BAIXO <= ALTO E NÃO achou FAÇA

medio TRUNCA(BAIXO+ALTO) / 2
SE VALOR < VETMATRIC[medio] ENTÃO
alto medio - 1
SENÃO SE valor > VETMATRIC[medio] ENTÃO
baixo medio + 1
SENÃO
achou VERDADEIRO
FIM-SE
FIM-ENQUANTO

SE achou ENTÃO
MOSTRE “pesquisa bem-sucedida !”
SENÃO
MOSTRE “pesquisa malsucedida !”
FIM-SE
FIM
FIM ALGORITMO
Para treinar
1. Se de um número de três algarismos subtrairmos sete, ele se
tornará divisível por 7. Se subtrairmos oito, ele se tornará
divisível por oito, e se subtrairmos nove, ele se tornará divisível
por nove. Qual é esse número?

2. Nas igualdades indicadas, os algarismos foram substituídos por


letras. Descubra, mediante raciocínio lógico, quais os números
que estão ocultos. Cada letra corresponde a um só algarismo.
DO + RE = MI
FA + SI = LA
RE + SI + LA = SOL

3. Colocados em coluna indiana (coluna por um), temos dois


índios e três brancos. Os índios falam sempre a verdade, os
brancos não. Não se pode vê-los. Podemos chamar dois deles
e, em outro local, fazer duas perguntas, uma a cada um. Após
isto, pode-se dizer a ordem em que estão colocados na fila.
Como?

4. Se as estrelas brilharem hoje à noite, amanhã o tempo estará


quente. Com efeito, hoje à noite as estrelas estão brilhando,
logo:
Amanhã o tempo não estará quente;
As estrelas brilharão amanhã à noite;
O tempo estará quente amanhã.
Capítulo 7

Modularização e
recursividade

Uma visão sem ação é somente um sonho. Uma ação sem visão
é apenas um passatempo. Uma visão com ação pode transformar o
mundo.
Joel Barker
Modularização
No fim da década de 1960, um conjunto de problemas no
desenvolvimento de sistemas de programação levou os países
desenvolvidos à chamada “crise do software”.
Os custos das atividades de programação mostravam, a cada
ano, uma clara tendência a se elevarem em relação aos custos dos
equipamentos. Essa tendência se devia, em grande parte, ao rápido
avanço tecnológico na fabricação dos equipamentos de
computação, em contrapartida à lenta evolução das técnicas
aplicadas ao desenvolvimento de software.
A ausência de uma metodologia para a construção de programas
conduz a um software geralmente cheio de erros e com alto custo
de desenvolvimento que, conseqüentemente, exige um custo
elevado para sua correção e manutenção futuras.
A programação estruturada foi o resultado de uma série de
estudos e propostas de disciplinas e metodologias para o
desenvolvimento de softwares. Conceitos associados, como técnica
de refinamentos sucessivos e modularização de programas,
integram o conjunto de ferramentas para a elaboração de
programas visando, principalmente, aos aspectos de confiabilidade,
legibilidade, manutenção e flexibilidade.
Pode-se reunir as idéias da programação estruturada em três
grupos:
Desenvolvimento de algoritmos por fases ou refinamentos
sucessivos;
Uso de um número muito limitado de estruturas de controle;
Transformação de certos refinamentos sucessivos em módulos.

Quando se desenvolve um algoritmo mediante refinamentos


sucessivos, faz-se uma opção pela divisão do algoritmo; este
procedimento conduz à modularização da solução do problema,
para dominar a complexidade e organizar o processo de
programação.
Um módulo é, então, um grupo de comandos, constituindo um
trecho de algoritmo, com uma função bem definida e o mais
independente possível em relação ao resto do algoritmo.
A técnica de modularização, associada aos refinamentos
sucessivos, vai permitir a redução da complexidade dos problemas.
Um bom algoritmo deve procurar reduzir a interação entre
módulos (acoplamento) e aumentar o relacionamento dos elementos
de um mesmo módulo (coesão).
Veja a seguir um exemplo de um algoritmo modularizado com
seus módulos refinados:
ALGORITMO
LEIA os dados do funcionário
Calculo _ vantagens {ativa o módulo cálculo das vantagens}
Calculo _ deduções {ativa o módulo cálculo das deduções}
SALARIOLIQUIDO VANTAGENS – DEDUÇÕES
MOSTRE SALARIOLIQUIDO
FIM ALGORITMO

MÓDULO calculo _ vantagens


SALARIOBRUTO NHORAS * SALARIOHORA
SALARIOFAMILIA NFILHOS * VALORPORFILHO
VANTAGENS SALARIOBRUTO + SALARIOFAMILIA
FIM MÓDULO

MÓDULO calculo _ deduções


INSS SALARIOBRUTO * 0,08
IRPF SALARIOBRUTO * TAXA
DEDUÇÕES INSS + IRPF
FIM MÓDULO

A descrição estrutural da modularização pode ser feita com o


diagrama hierárquico, como vemos a seguir:

Figura 7.1.
A maneira mais intuitiva de proceder à modularização de
problemas é definindo-se um módulo principal de controle e
módulos específicos para as funções do algoritmo. No diagrama
anterior, o módulo principal tem a função de receber os dados,
escrever os resultados e exercer o controle na execução das
funções do algoritmo. A determinação das vantagens e deduções é
delegada a módulos específicos.
As linguagens de programação hoje existentes dispõem de
recursos que facilitam a construção e manipulação de módulos.
Estes recursos permitem não só a modularização dos comandos do
programa, mas também a modularização dos dados utilizados.
A experiência recomenda que os módulos de um programa
devem ter um tamanho limitado. Módulos muito grandes são de
difícil compreensão e, em geral, multifuncionais.
Um outro aspecto importante é a possibilidade de cada módulo
poder definir as próprias estruturas de dados, suficientes e
necessárias apenas para atingir o objetivo final do módulo.
Todo módulo é constituído por uma seqüência de comandos que
operam sobre um conjunto de objetos, que podem ser globais ou
locais:
Objetos globais são entidades que podem ser usadas em
módulos internos a outro módulo do algoritmo em que foram
declaradas;
Objetos locais são entidades que só podem ser usadas em
módulos do algoritmo no qual foram declaradas. Estes objetos
não possuem qualquer significado fora deste módulo.

Exemplos de objetos globais ou locais: variáveis, arquivos, outros


módulos etc.
Um módulo pode usar objetos globais ou locais em relação a ele.
Porém, não pode usar objetos declarados em módulos que não o
abrangem. Isso significa que objetos globais, declarados em
módulos mais externos ou mesmo ao nível do módulo principal,
também podem ser utilizados em módulos mais internos.
A comunicação entre os módulos deverá ser feita por vínculos,
utilizando-se objetos globais ou transferência de parâmetros.
A decisão pela divisão do algoritmo em módulos traz benefícios
como os seguintes:
A independência do módulo permite uma manutenção mais
simples e evita efeitos colaterais em outros pontos do algoritmo;
A elaboração do módulo pode ser feita independentemente e
em época diferente do restante do algoritmo;
Testes e correções dos módulos podem ser feitos em separado;
Um módulo independente pode ser utilizado em outros
algoritmos que requeiram o mesmo processamento por ele
executado, o que nos leva ao conceito técnico de reusabilidade
de códigos de programas.
Ferramentas para modularização
Entre as ferramentas de modularização, podemos destacar as
sub-rotinas e as funções, que nada mais são do que módulos de
programação que servem basicamente a três objetivos:
Evitar que certa seqüência de comandos, necessária em vários
locais de um algoritmo, tenha de ser escrita repetidamente
nestes locais;
Dividir e estruturar um algoritmo em partes fechadas e
logicamente coerentes;
Aumentar a legibilidade de um algoritmo.

A divisão de um algoritmo em módulos, além de facilitar sua


elaboração, permite uma melhor documentação e verificação de sua
correção.
Cada módulo, implementado como uma sub-rotina ou uma
função, deve conter sua própria documentação e pode ser verificado
independentemente. Isso torna eficiente o trabalho das equipes de
programação, já que um algoritmo pode ser dividido entre vários
programadores, que podem implementar, testar e catalogar suas
respectivas partes, separadamente.
Sub-rotinas e funções são módulos hierarquicamente
subordinados a um algoritmo, comumente chamado módulo
principal.
Da mesma forma, uma sub-rotina, ou uma função, pode conter
outras sub-rotinas e funções aninhadas, como pode ser visto no
diagrama hierárquico a seguir:
Figura 7.2.

O infográfico anterior ilustra um algoritmo constituído por um


módulo principal MP, as sub-rotinas S1, S2, S3 e S4 e as funções
F1, F2, F3 e F4.
A sub-rotina e a função são criadas mediante suas declarações
em um algoritmo. Para serem executadas, precisam da ativação
provocada por um comando de chamada.
A declaração de uma sub-rotina ou função é constituída por um
cabeçalho e por um corpo. O cabeçalho, que identifica a sub-rotina
ou função, contém seu nome e a lista de parâmetros formais. O
corpo contém declarações locais e os comandos da sub-rotina ou
função.
A ativação de uma sub-rotina ou função é feita por meio da
referência a seu nome e pela indicação dos parâmetros atuais.

Sub-rotina (procedimentos)

Uma sub-rotina é declarada como vemos a seguir:


SUBROTINA Nomesub (lista _ de _ parâmetros _ formais)
VAR
{Declarações das variáveis locais à sub-rotina}

bloco de instruções
FIM SUBROTINA

A chamada de uma sub-rotina é feita com uma referência a seu


nome e pela indicação dos parâmetros atuais no local do algoritmo
em que a sub-rotina deve ser ativada, ou seja, no local em que sua
execução deve ser iniciada. A forma geral para o comando de
ativação de uma sub-rotina é a seguinte:
Nome lista _ de _ parâmetros _ atuais

Ao terminar a execução dos comandos de uma sub-rotina, o fluxo


de controle retorna ao comando seguinte aquele que provocou a
chamada. Dessa maneira, a execução de uma sub-rotina se
constitui em uma transferência temporária da execução do módulo
habilitador para a sub-rotina, retornando, depois, ao algoritmo que a
chamou. Observe:
ALGORITMO calc _ media
VAR
Nt1, nt2 : real
Media : real
INÍCIO
Monta _ tela verde
{chama a sub-rotina monta _ tela e define qual será a cor
escolhida para seu fundo como parâmetro}
LEIA nt1,nt2
media (nt1+nt2) / 2
MOSTRE media
FIM

Subrotina Monta _ tela (cor : string)


INÍCIO
limpar tela
corfundo cor
MOSTRE “Escola ABC da vida”
MOSTRE “Sistema de Controle de Notas – 2005”
FIM

Função

As funções, embora bastante semelhantes às sub-rotinas, têm a


característica especial de retornar ao algoritmo que as chamou um
valor associado ao nome da função. Esta característica permite uma
analogia com o conceito de função da matemática.
No Capítulo 2, quando apresentamos as expressões aritméticas,
foram usadas algumas funções que já são consideradas como
fundamentais em notação algorítmica, o que adotamos neste livro,
como por exemplo RESTO, TAMANHO etc.
A utilização de outras funções no algoritmo, como fatorial, ou
qualquer outra função especial, pode ser feita declarando-se uma
função escrita pelo próprio programador.
A declaração de uma função é idêntica à de uma sub-rotina, com
exceção de que é necessário definir seu tipo de dado, ou seja, o tipo
do valor que será retornado. Além de numéricas, as funções podem
ser lógicas ou de texto.
Apresentamos, a seguir, a forma de declaração das funções:
FUNÇÃO NomeFunção(Lista-de-Parâmetros-Formais): TipoValor-
Retornado
VAR
{Declarações das variáveis locais à função}

bloco de instruções

FIMFUNÇÃO

A chamada de uma função é feita com a atribuição da referência a


seu nome e pela indicação dos parâmetros atuais a uma variável do
mesmo tipo de dado do retorno da função. Veja a forma geral para a
ativação de uma função:
VAR NomeFunção(Lista-de-Parâmetros-Atuais)

em que:
VAR: é o nome da variável que receberá o retorno da função;
NomeFunção: é o nome dado à função;
Lista _ de _ Parâmetros _ Atuais: é a lista de variáveis que
substituirão os parâmetros formais durante a execução da
função. Os parâmetros atuais devem concordar em número,
ordem e tipo de dados com os parâmetros formais.
O fluxo de controle é desviado para a função no momento em que
ela é ativada. Ao terminar a execução dos comandos da função, o
fluxo de controle retorna ao comando seguinte aquele no qual ela foi
ativada:
ALGORITMO verifica _ numeros
VAR
Valor,CONT : inteiro
soma : inteiro
INÍCIO
CONT 1
ENQUANTO CONT <= 50 FAÇA
LEIA valor
TipoValor Verifica _ numero(valor)
{chama a função verifica _ numero a qual retornara se o valor
lido é par ou ímpar}
MOSTRE TipoValor
Soma soma + valor
CONT cont + 1
FIM-ENQUANTO

FIM
Função Verifica _ numero (val : inteiro) : texto
INÍCIO
SE RESTO(val,2) = 0 ENTÃO
verifica _ numero “O número lido é Par”
SENÃO
verifica _ numero “O número lido é Ímpar”
FIM-SE
FIM
Parâmetros
Parâmetros são como variáveis de memória usadas para manter
uma comunicação entre o módulo principal e os módulos
secundários, enviando valores para serem transformados, ou
ajudando a definir as ações que o módulo secundário deve executar
mediante o teste de seus valores.
Os parâmetros podem ser passados a uma função ou a uma
subrotina de dois modos: por valor ou por referência.
A vinculação entre módulos pode ser feita mediante a
transferência, ou passagem, de parâmetros, que associam
parâmetros atuais com os parâmetros formais.
Vejamos os modos de transferência de parâmetros.

Passagem de parâmetros por valor

Na passagem de parâmetros por valor, as alterações feitas nos


parâmetros formais, dentro da sub-rotina/função, não se refletem
nos parâmetros atuais. O valor do parâmetro atual é copiado no
parâmetro formal, na chamada da sub-rotina/função, ou seja, uma
cópia do conteúdo da variável de memória do módulo principal é
enviada ao módulo secundário e armazenada em sua variável
receptora. A partir daí, a variável receptora pode ter seu valor
alterado, sem que ocorra qualquer mudança no valor da variável
original mandada como parâmetro. A passagem de parâmetros por
valor é feita na forma padrão. Por isso, não há nenhuma mudança
na forma de declaração da sub-rotina/função.

Passagem de parâmetros por referência

Na passagem de parâmetros por referência, toda alteração feita


num parâmetro formal corresponde à mesma alteração feita no
parâmetro atual associado. O endereço da variável de memória é
enviado ao módulo e armazenado na variável receptora. Desse
modo, em vez de termos dois endereços de memória distintos, com
conteúdos iguais, temos um único endereço de memória
referenciado com dois nomes diferentes.
Sendo assim, qualquer alteração que ocorrer na variável
receptora ocorrerá também na variável de memória mandada como
parâmetro, pois, apesar de terem nomes diferentes, ambas
referenciam o mesmo endereço de memória.
Veja a representação da passagem de parâmetros por referência:
SUBROTINA Nomesub (Var _ men Por Refer ; lista _ de _
parâmetros _ formais)

FUNÇÃO NomeFunção(Var _ men Por Refer ; lista _ de _ parâmetros


_ formais) : TipoValorRetornado

Como está demonstrado, é necessário a inclusão da cláusula Por


Refer dentro da declaração de parâmetros para indicar quais são os
parâmetros passados por referência; depois, seguem-se os demais
parâmetros.
Ao se elaborar uma sub-rotina/função, deve-se tomar cuidado ao
classificar os parâmetros entre os tipos mencionados, para evitar os
efeitos colaterais decorrentes do modo de passagem utilizado.
Finalmente, é importante ressaltar que nas linguagens de
programação há, muitas vezes, mais de um modo de passagem de
parâmetros com regras específicas, para indicar qual o modo que se
aplica a cada parâmetro, sendo necessário, às vezes, realizar
adaptações no algoritmo.
Algoritmos recursivos
(recursividade)
Recursividade é a capacidade que uma sub-rotina/função tem de
existir em razão de si própria, ou seja, diz-se que uma sub-
rotina/função é recursiva quando ela solicita a si própria dentro do
bloco de suas instruções.
A recursividade deve ser usada apenas em situações nas quais é
necessário que a sub-rotina/função precise repetir todas as suas
próprias instruções para solucionar um problema. Além disso, é
preciso ter total certeza de que as chamadas recursivas terão um
fim, caso contrário a sub-rotina/função entrará em um ciclo infinito
de chamadas, ou seja, a execução do algoritmo ficará presa em um
loop infinito.
Um exemplo de recursividade é o caso da função fatorial usada
em matemática. Fatorial é a multiplicação de um número por todos
os números anteriores a ele e maiores que zero.
Generalizando, o fatorial de um número inteiro N qualquer é igual
a esse número multiplicado pelo fatorial de seu antecessor,
chegando-se à seguinte fórmula:

N! = N*(N-1)!

De posse dessa fórmula, pode-se desenvolver o algoritmo de uma


função que calcule o fatorial de um número inteiro N qualquer:
FUNÇÃO Fatorial (numero : INTEIRO) : INTEIRO

Retorno : INTEIRO
Retorno 0
SE numero > = 0 ENTÃO
SE numero = 1 OU numero = 0 ENTÃO
Retorno 1 {o fatorial de 1 e 0 é igual a 1}
SENÃO
Numero numero * Fatorial(numero - 1)
Retorno numero
FIM-SE
FIM-SE
Fatorial Retorno {Retorno da função}
FIM-FUNÇÃO
Para treinar
1. Se a média aritmética de cinco números inteiros consecutivos é
17, o menor dos cinco números é:
11;
12;
13;
14;
15.

2. As roupas de Lúcia, Ana e Marta para a festa do próximo fim de


semana são, não necessariamente nesta ordem, um vestido,
um conjunto com saia e um conjunto com calça. Uma das
roupas é preta, a outra, azul, e a última, branca. A roupa preta é
de Lúcia; a roupa da Marta é um conjunto com calça; a roupa
da Ana não é azul e não é o vestido. As cores do vestido, do
conjunto com saia e do conjunto com calça são,
respectivamente:
Preta, branca e azul;
Branca, preta e azul;
Branca, azul e preta;
Preta, azul e branca;
Azul, branca e preta.

3. Um caixa eletrônico trabalha apenas com notas de R$ 50,00 e


R$ 100,00. Se uma pessoa tirou doze notas, num total de R$
800,00, a quantidade de notas de R$ 100,00 é:
2;
4;
5;
6;
8.
Capítulo 8

Arquivo de dados e estrutura


de dados complexa

A amizade começa quando, estando juntas, duas pessoas podem


permanecer em silêncio sem se sentirem constrangidas.
Tyson Gentry
Trabalhando com arquivo de dados
Para colocarmos informações em periféricos de armazenamento
auxiliar (HD, disquete, CD etc.), é necessário que essas
informações estejam organizadas sob a forma de arquivos ou
tabelas em SGBDs, sigla de Sistema Gerenciador de Banco de
Dados.
Um arquivo de dados, ou uma tabela de dados, armazena um
conjunto de informações de mesma natureza ou origem, como, por
exemplo:
Informações sobre os alunos de uma universidade (arquivo de
alunos ou tabela de alunos);
Informações sobre locações de fitas de vídeo de uma
videolocadora (arquivo de locações ou tabela de locações).

Existem duas formas básicas de trabalhar com arquivos:


Leitura de informações existentes (armazenadas);
Gravação de novas informações no arquivo/tabela.

A unidade para operação com arquivos de dados, ou seja, a


unidade de informação usada quando gravamos/lemos um arquivo
de dados é chamada registro e, nas tabelas, é conhecida como
linha, ou tupla.
O registro pode ser definido como um grupo de informações sobre
determinado elemento do arquivo. As informações nos
registros/linhas estão organizadas em campos/colunas, em que
cada campo/coluna representa uma informação atômica (não
divisível) de um arquivo de dados.
Como exemplo, para o arquivo Alunos:
O registro seria Matrícula, Nome, Sexo, DataNasc, Telefone e
Turma;
O campo seria Nome.
Operações sobre arquivos ou
registros
Para que um programa possa utilizar as informações de um
arquivo, existe um conjunto de operações básicas. Este conjunto de
operações básicas possui dois grandes grupos, vistos nos tópicos
seguintes.

Operações no nível do arquivo de dados

São as operações que fazem que um programa possa usar um


arquivo de dados. As ações possíveis são:
Abertura;
Fechamento;
Teste de Final (EOF) e Início (BOF) de arquivo.

Operações no nível do registro/linha


Leitura: transfere as informações (um registro/coluna) do
arquivo/tabela (da memória auxiliar) para o programa (memória
principal);
Gravação: cria um novo registro no arquivo/tabela na memória
principal;
Alteração/Regravação: altera as informações de um
campo/coluna que já se encontra gravado no arquivo/tabela;
Deleção: remove um registro/linha de um arquivo/tabela.

Organização x acesso

Os arquivos de dados armazenados em periféricos de E/S diferem


quanto a sua forma de organização e recuperação dos registros.
Apresento-lhes as formas mais utilizadas e os respectivos modos de
acessar registros em cada uma das organizações.

Organização seqüencial
Nesta forma, os registros estão fisicamente arrumados, um após o
outro. É a forma mais simples de organização.
Para trabalhar com arquivos seqüenciais, devemos, sempre,
acessar cada registro em sua ordem física, ou seja, caso
precisemos das informações do décimo registro, devemos ler os
registros anteriores (do primeiro ao nono) até encontrar o registro
desejado.
Para gravar um novo registro, isso deve ser feito após o último
registro anteriormente existente.
Pelo que foi dito, é fácil compreender que a organização
seqüencial é recomendável quando se deseja processar uma
grande parte dos registros na ordem como estão gravados. Esse
tipo de organização, na maioria das vezes, é utilizado em
processamentos do tipo batch.

Organização relativa
A organização relativa difere da seqüencial na forma de acesso
aos registros. Nesse modo de organização, o programa pode
acessar um registro pela sua posição física no arquivo, isto é, se
precisarmos ler o décimo registro do arquivo de dados, o programa
pode acessálo diretamente, sem ter de acessar antes nenhum outro
registro.

Organização indexada
Nesta forma de organização, os registros são gravados em forma
aleatória. Para acessar um registro do arquivo de dados, isso deve
ser feito por um dos campos do registro, denominado de campo
chave ou índice.
Essa organização cria dois arquivos separados, relacionados
(interligados) pela chave primária. O primeiro arquivo, o arquivo de
índices, contém os valores do campo chave primária do arquivo de
dados e o endereço físico correspondente àquela chave primária no
arquivo de dados original. O outro arquivo denominado arquivo de
dados contém os dados em registros propriamente ditos.
As operações (consulta, inclusão, alteração e exclusão) sobre os
registros são feitas mediante o arquivo de índices, menor e mais
fácil de manipular.
Esse tipo de acesso difere do seqüencial, pois não precisamos ler
os registros anteriores, e do relativo, porque o registro é identificado
por uma chave lógica, e não por sua posição física no arquivo de
dados.
Noções de organização de dados
na memória principal
Os elementos necessários ao processamento estão
sistematicamente organizados na memória. Dessa forma, é
importante que esses elementos (dados) estejam convenientemente
organizados, de forma a permitir um acesso rápido e menor perda
possível de espaço de memória.
Principais estruturas de dados
Entre as formas de organizar os dados, destacam-se três, que
veremos adiante. Vale ressaltar que a escolha de uma ou de outra
estrutura deve ser feita com base nas necessidades de velocidade
de acesso e no espaço de memória disponível. Algumas dessas
estruturas não são usadas diretamente, na maioria dos casos,
porém, elas servem de suporte a outras estruturas usadas
corriqueiramente.

Estrutura de dados: pilha (stack)

É uma estrutura de armazenamento na qual as inserções e


retiradas de elementos são feitas no início da lista, ou seja, ocorrem
em uma única posição, que chamaremos topo da pilha. É muito
semelhante a pilhas de pratos, livros, cartas etc., nas quais só
teríamos acesso ao elemento do topo; caso contrário, a pilha
desabaria. Para ter acesso a um livro que está na parte de baixo do
topo, temos de retirar, primeiro, os que estão em cima.
O acesso aos elementos da pilha segue o esquema conhecido
por LIFO (Last In First Out), algo como: o primeiro a entrar é o último
a sair. O infográfico a seguir ilustra esse esquema:
Figura 8.1.

Pilhas são estruturas muito dinâmicas, que crescem e decrescem


de maneiras muitas vezes imprevisíveis durante a execução do
programa. Quando é possível prever o tamanho máximo de uma
pilha, ela pode ser representada por um vetor. Para amarrar
logicamente o topo ao vetor de elementos, podemos declarar a pilha
como um registro, que contém topo e elementos, da seguinte
maneira:
TIPO pilha = REGISTRO
Topo : INTEIRO
Elementos : VETOR[1..N] DE REAL
FIM-REGISTRO
pilha : P

Sendo que N é o número máximo de elementos que a pilha P


pode conter; é o tamanho dela (size).

Terminologias para a manipulação de pilhas

O topo da pilha é o elemento ao qual se tem


Topo
acesso imediato.

Empilhar Significa colocá-lo no topo da pilha.

Desempilhar Significa removê-lo do topo da pilha.

O fundo da pilha é o elemento ao qual só


Fundo temos acesso após desempilhar todos os
outros elementos.

Tabela 8.1.
Operações realizadas sobre as pilhas

Teste de pilha cheia


Compara-se o topo com o tamanho da pilha:
SE P.Topo = N ENTÃO
MOSTRE “Pilha P Cheia !!”
FIM-SE

Teste de pilha vazia


Compara-se o topo com zero:
SE P.Topo = 0 ENTÃO
MOSTRE “Pilha P Vazia !!”
FIM-SE

Empilhamento (push)
Realiza a inserção de um item I na pilha P. O topo é incrementado
e o novo elemento é colocado na posição indicada pelo novo valor
do topo:
SUBROTINA Push (P, I, N)

SE P.Topo = N ENTÃO
MOSTRE “Pilha P Cheia !!”
SENÃO
P.Topo P.Topo + 1
P.Elementos[P.Topo] I
FIM-SE

FIM SUBROTINA

Desempilhamento (pop)
Realiza a retirada de um elemento da pilha para obedecer à
restrição imposta a esta estrutura, do tipo LIFO. O elemento a ser
excluído é sempre o do topo:
SUBROTINA Pop (P, J)

SE P.Topo = 0 ENTÃO
MOSTRE “Pilha P Vazia !!”
SENÃO
J P.Elementos[P.Topo]
P.Topo P.Topo - 1
FIM-SE

FIM SUBROTINA

Visita à pilha
Consiste em obter o valor do elemento que está no topo da pilha
no momento da visita:
SUBROTINA Visita (P, J)

SE P.Topo = 0 ENTÃO
MOSTRE “Pilha P Vazia !!”
SENÃO
J P.Elementos[P.Topo]
FIM-SE

FIM SUBROTINA

Estouro de pilha (overflow)


É a tentativa de realizar uma operação push quando P.Topo = N.

Underflow
É a condição contrária à do overflow: é a tentativa de realizar uma
operação pop quando P.Topo = 0.

Inicialização
Acontece quando uma pilha representada por vetor é inicializada
com P.Topo = 0.
Esta estrutura é bastante usada nas linguagens de programação
para implementar o uso de subprogramas.
Quando um subprograma “chama” o outro, é guardada a posição
de retorno e, quando ocorre um retorno, o fluxo de execução é
passado para esta posição:

Figura 8.2.
Estrutura de dados: fila (queue)

É uma estrutura de armazenamento em que as inserções ocorrem


na posição final e as retiradas, na posição inicial.
Este critério não ocorre só em computação. É o mesmo que rege
uma fila de pessoas no caixa do banco, por exemplo. As pessoas
são atendidas na ordem em que chegam. O atendimento de alguém
que esteja na terceira posição da fila só é feito, em condições
normais, após ter sido atendido quem ocupava a primeira e a
segunda posições, nesta ordem.
O acesso aos elementos de uma fila segue o esquema FIFO (First
In First Out), algo como: o primeiro a entrar é o primeiro a sair.
A seguir, ilustramos este esquema:

Figura 8.3.

Esta estrutura tem grande utilização na computação, entre elas,


podemos destacar seu uso nos sistemas operacionais que
organizam os programas em uma fila e os submete à CPU,
dependendo da ordem de chegada (sistema operacional batch).
As filas, tal como as pilhas, são estruturas dinâmicas, que
crescem e decrescem de modo imprevisível durante a execução do
programa. Quando seu tamanho máximo é previsível, ela pode ser
representada por um vetor. Com o objetivo de amarrar logicamente
os índices ao vetor de elementos, podemos declarar a fila como um
registro, com começo, término e elementos, como vemos a seguir:
TIPO fila = REGISTRO
Termino : INTEIRO
Elementos : VETOR[1..N] DE REAL
Comeco : INTEIRO
FIM-REGISTRO
fila : F
Sendo que N é o número máximo de elementos que a fila F pode
conter.

Terminologias para a manipulação de filas

Entrar
Entrar com um elemento na fila significa colocá-
lo no fim da fila.
Sair
Sair da fila significa retirar um elemento do início
da fila.

Um elemento está esperando na fila sempre


Esperando
que lhe pertencer.

Tabela 8.2.

Operações realizadas em filas

Teste de fila cheia


Compara-se o Termino com o tamanho N:
SE F.Termino = N ENTÃO
MOSTRE “Fila F Cheia !!”
FIM-SE

Teste de fila vazia


Compara-se o Comeco com o Termino. A fila só estará vazia se o
Termino for menor que Comeco:
SE F.Termino < F.Comeco ENTÃO
MOSTRE “Fila F Vazia !!”
FIM-SE

Inicialização da fila
F.Comeco 1
F.Termino 0
Esta inicialização garante a condição necessária para tornar
verdadeiro o teste de fila vazia.

Inserção de elemento na fila


É feita na extremidade Termino:
SE F.Termino = N ENTÃO
MOSTRE “Fila F Cheia !!”
SENÃO
F.Termino F.Termino + 1
F.Elementos[F.Termino] I
FIM-SE

Remoção de elemento na fila


É feita na extremidade Comeco:
SE F.Termino < F.Comeco ENTÃO
MOSTRE “Fila F Vazia !!”
SENÃO
J F.Elementos[F.Comeco]
F.Comeco F.Comeco + 1
FIM-SE

Visita à fila
Consulta a obtenção do valor do elemento do começo da fila sem
removê-lo:
SE F.Termino < F.Comeco ENTÃO
MOSTRE “Fila F Vazia !!”
SENÃO
J F.Elementos[F.Comeco]
FIM-SE

Overflow
Ocorre quando se deseja inserir um elemento na fila e não é mais
possível fazê-lo, pois F.Termino = N.

Underflow
É a condição contrária à do overflow, isto é, deseja-se remover
um elemento de uma fila vazia (F.Termino < F.Comeco).
Observação: a igualdade de valores entre F.Comeco
e F.Termino indica que na fila só existe um elemento.

Estrutura de dados: lista

A estrutura de dados lista é bastante utilizada pelas linguagens de


programação e, também, pelos próprios programadores,
diretamente.
Existem vários tipos de listas, mas a que me refiro é a chamada
lista encadeada. Ela consiste em um arranjo de vários elementos de
forma que cada um deles “aponta” para seu posterior, como
ilustrado a seguir:

Figura 8.4.

Na verdade, cada elemento da lista é dividido em dois: um com a


informação armazenada, e o outro com a posição do próximo
elemento.
As listas encadeadas são muito usadas para fazer alocação
dinâmica de posições de memória. Isso é interessante quando
temos uma quantidade desconhecida de dados. Quando chega um
elemento, ele é alocado (reservado em um espaço de memória) na
hora, e por isso se diz que a alocação é dinâmica. Com isso, não
precisamos fazer uma pré-alocação, como é o caso dos vetores, o
que evita desperdícios de memória.
O uso de listas é mais complexo do que o uso de vetores (por
exemplo), e, quando utilizadas inadequadamente, pode gerar
demora no acesso aos elementos, bem como gasto excessivo de
memória, pois além de guardar o conteúdo dos elementos, guarda-
se, também, as localizações destes elementos. Aos elementos em
uma lista linear encadeada damos o nome de nó, e podemos
colocá-los em qualquer ponto da memória, ou seja, eles não
precisam estar fisicamente adjacentes.
Um nó é uma célula, um elemento válido de uma lista encadeada,
que contém pelo menos um campo para armazenar informação útil
e pelo menos um campo link, com o ponteiro, que é o endereço do
próximo nó, ou o elo de ligação entre eles.
Assim, cada nó da lista é composto por um registro contendo N >
0 campos para a informação propriamente dita e N > 0 campos para
guardar o(s) endereço(s) do(s) próximo(s) nó(s).
O primeiro nó de uma lista é tratado como caso especial, pois é o
único acesso correto que se tem a ela. Como não há nó precedente
ao primeiro, ele não pode ser acessado da mesma maneira que os
outros nós da lista.
O último nó é chamado Sentinela, ou Delimitador. O valor de seu
campo link é Nil.
Embora seja possível usarmos vetores para implementar listas
encadeadas, a versão com ponteiros é mais utilizada. Veja:

Figura 8.5.
Para treinar
1. Considerando verdadeiras as seguintes proposições:
Se João cometeu um grave delito, ele sonegou impostos;
João não sonegou impostos.

Pode-se concluir que:


João sonegou impostos;
João cometeu um grave delito;
João cometeu um grave delito e sonegou impostos;
João cometeu um grave delito;
João cometeu um grave delito, ou sonegou impostos.

2. Considere a proposição: Paulo é elegante, ou Paulo é alto e


moreno. Como Paulo não é elegante, então, conclui-se que:
Paulo não é alto e não é moreno;
Paulo não é alto ou não é moreno;
Paulo é alto e moreno;
Paulo é alto ou moreno;
Paulo é alto e não é moreno.

3. Hoje, A e B estão de folga do trabalho. Sabendo-se que A tem


folga de seis em seis dias e B, de quatro em quatro dias, e que
a folga dos dois coincide, sempre, a cada x dias, pode-se
concluir que o valor de x é:
4;
6;
10;
12;
18.
Capítulo 9

Mapeamento de
pseudocódigo e comandos da
linguagem Turbo Pascal

A persistência é o caminho do êxito.

Charles Chaplin
Mapeamento de pseudocódigo com
algumas linguagens de
programação
Será exibido adiante um simples exemplo de pseudocódigo que
trabalha com três variáveis, com as quais é calculada a média
aritmética de duas notas, para, depois, emitir seu resultado. O
referido pseudocódigo será codificado em cinco diferentes
linguagens de programação estruturadas, para que o leitor tenha
uma idéia de implementação de algoritmos computacionais:
ALGORITMO calcmedia
VAR
Nt1,nt2,media : inteiro
INÍCIO
Nt1 10
Nt2 2
Media (nt1 + nt2)/ 2
MOSTRE media
FIM
FIM ALGORITMO

Veja sua apresentação na linguagem de programação Pascal


PROGRAM calcmedia;
USES CRT;
VAR
nt1,nt2,media : integer;
BEGIN
CLRSCR;
NT1 := 10;
NT2 := 2;
media := (nt1 + nt2) / 2;
WRITELN(‘A média foi ‘ , media:5:2);
READKEY;
END.

Agora, em C:
MAIN( )
{
FLOAT nt1,nt2, media;
Nt1 = 10;
Nt2 = 2;
Media := (nt1 + nt2) / 2;
PRINTF(%D, media);

Em Cobol, temos:
IDENTIFICATION DIVISION.
PROGRAM-ID. calcmedia.

ENVIRONMENT DIVISION.

DATA DIVISION.
WORKING-STORAGE SECTION.
77 nt1 PIC 99 VALUE 10.
77 nt2 PIC 99 VALUE 2.
77 media PIC 99 VALUE ZEROS.

PROCEDURE DIVISION.
ROT01.
COMPUTE media = (nt1 + nt2) / 2
DISPLAY media
STOP RUN.

Em Clipper:
PROCEDURE MAIN( )
Nt1 = 10
Nt2 = 2
media = (nt1 + nt2) / 2
@ 10, 5 SAY “A média foi “
@ 10, 16 SAY media PIC “999”
INKEY(5)
RETURN

E, por último, em Basic:


10 DIM nt1%,nt2%,media%
20 nt1 = 10
30 nt2 = 2
40 media = (nt1 + nt2) / 2
50 PRINT media
60 END
Mapeamento de pseudocódigo
para Turbo Pascal 7.0
Estrutura de um programa Pascal

PROGRAM NomeProg ;

VAR
Nomevar : TIPO ;
BEGIN

END.

Declaração de variáveis
VAR
Var1 : TIPO;

Sendo que Tipo pode ser :

Integer, Real, Boolean(lógico), String(Texto), etc.

Atribuição

No lugar do símbolo será utilizado o sinal :=:

Var1 := ‘Teste atribuição’;

As aspas duplas (“ “) serão substituídas por aspas simples (‘ ‘).

Comandos de entrada e saída de dados

Entrada de dados
READLN(‘Texto Descritivo’ , Var1);
Muda de linha para a nova entrada de dados.
READ(‘Texto Descritivo’ , Var1);

Não muda de linha para a nova entrada de dados.

Saída de dados
WRITELN(‘Texto Descritivo’, Var1);

WRITELN(‘Texto Descritivo’);

Estrutura condicional
IF Condição THEN
BEGIN
C1;
C2;
C3;
END
ELSE
C4;

Observação: será necessário inserir os comandos


BEGIN e END dentro das cláusulas THEN e ELSE
somente se elas possuírem mais de uma linha de
comando. No exemplo anterior, a opção THEN possui
três comandos, logo ela utilizou os comandos
BEGIN/END, mas a opção ELSE não precisou utilizar,
por possuir somente um comando interno. Outra
observação relevante é o fato de não colocar END
antes de um comando ELSE.

Repetição com teste no início


WHILE Expressão DO
BEGIN
C1;
C2;
C3;
END;

Repetição com teste no final


REPEAT
C1;
C2;
C3;
UNTIL Expressão;

Observação: não é necessário utilizar o bloco


BEGIN/END quando existir mais de uma linha de
comando dentro do laço do REPEAT.

Repetição com variável de controle

Em primeiro lugar, quando o passo for igual a 1:


FOR var := exp1 TO exp2 DO
BEGIN
C1;
C2;
C3;
END;

Depois, quando o passo for igual a -1:


FOR var := exp1 DOWNTO exp2 DO
BEGIN
C1;
C2;
C3;
END;

Condicional composta
CASE expressão OF
VALOR1 : C1;
VALOR2 : C2;
VALOR3 : C3;
ELSE
C4;
END;

Vetores

A sintaxe de vetores é a seguinte:


VAR
Vet1 : ARRAY [DimMin .. DimMax] OF Tipodedado;

Veja um exemplo:
VAR
VetCodigo : ARRAY[1..40] OF INTEGER;

Matriz

A sintaxe de matrizes é a seguinte:


VAR
Matr1 : ARRAY [DimMin .. DimMax , DimMin .. DimMax] OF
Tipodedado;

Veja um exemplo:
VAR
MatCurso : ARRAY[1..4 , 1..3] OF STRING;

Registros

A sintaxe dos registros é a seguinte:


TYPE
NomeReg : RECORD
Elemento1 : TipodeDado;
Elemento2 : TipodeDado;
END;
VAR
NovonomeReg : NomeReg;

Veja um exemplo:
TYPE
Alunos : RECORD
Notas : ARRAY[1..4] OF REAL;
Media : REAL;
Conceito : STRING[10];
Nome : STRING[30];
END;
VAR
Estudante : ALUNOS;

Algumas funções gerais


Textcolor(blue);: cor da fonte azul;
Textbackground(red);: cor do fundo da tela em vermelho;
Length(var);: tamanho da variável;
Copy(var,1,3);: retorna três caracteres à esquerda da variável
(simula as funções esquerda, direita e subcadeia);
AND/OR: operadores lógicos E/OU.

Observação: a combinação das teclas CTRL + F9


executa uma aplicação dentro do Pascal.
Exemplo prático completo
Problema

Vamos elaborar um algoritmo que calcule uma equação do


segundo grau. Para isso, o programa deverá receber as variáveis da
equação (a, b e c) e, com isso, calcular as raízes x1 e x2. O
programa deverá, também, verificar o valor de Delta e exibir as
mensagens de acordo com:
Delta maior do que zero: impossível de calcular;
Delta igual a zero: raiz única x1 = x2;
Delta maior do que zero: raízes distintas.

Observação: o programa deverá calcular o valor das


raízes de N equações, encerrando a execução quando
o usuário escolher sair.

Solução em forma de pseudocódigo


ALGORITMO Equacao
VAR
X1,x2, a, b, c, delta : real
INÍCIO
REPITA
LEIA a, b, c
Delta b ** 2 – 4*a*c
SE Delta < 0 ENTÃO
MOSTRE “Impossível Calcular Delta Negativo”
SENÃO
X1 -b + (raiz(delta) / (2*a))
SE Delta = 0 ENTÃO
MOSTRE “Raiz única”
MOSTRE x1
SENÃO
X2 -b – (raiz(delta) / (2*a))
MOSTRE “Raízes Distintas”
MOSTRE x1, x2
FIM-SE
FIM-SE
LEIA “Deseja encerrar o programa”, resp

ATÉ-QUE resp = “Sim”


FIM

Implementação na linguagem Pascal


program Equacao;
uses crt; {biblioteca do Pascal que permite usar os comandos
Clrscr e readkey}
Var
X1,x2, a, b, c, delta : real;
Resp : string;
Begin
Clrscr; {limpa a tela}
Repeat
Writeln(‘Informe o valor de A :’);
Readln(a);
Writeln(‘Informe o valor de B :’);
Readln(b);
Writeln(‘Informe o valor de C :’);
Readln(c);
Delta := sqr(b) - 4*a*c;

If delta < 0 then


Writeln(‘Impossível Calcular Delta Negativo’)
Else
begin
X1 := -b + (sqrt(delta) / (2*a));
if delta = 0 then
begin
writeln(‘Raiz única’);
writeln(‘O valor de x1 = ‘, x1:4:2);
end
else
begin
X2 := -b –(sqrt(delta) / (2*a));
Writeln(‘Raízes Distintas’);
Writeln(‘O valor de x1 =’, x1:4:2);
Writeln(‘O valor de x2 =’, x2:4:2);
End;
End;
Readkey; {dá um tempo para o usuário ver o resultado}
Writeln(‘Deseja encerrar o programa’);
Readln(resp);

Until (resp = ‘sim’);


End.
Para treinar
1. Para a comemoração da nomeação de seu chefe, Luciana
encomendou a um serviço de festas sete salgadinhos para
cada convidado. Ao receber os salgadinhos, notou que havia
dois a mais que o encomendado. Notou também que
compareceram três pessoas a mais do que o esperado. Luciana
distribuiu todos os salgadinhos e cada pessoa presente recebeu
seis. Com base nessas informações, pode-se afirmar que o
número total de salgadinhos recebidos foi:
112;
114;
118;
124;
128.

2. Considere as seguintes proposições simples:


p: João vai ao clube;
q: Hoje é domingo.

A proposição composta, ~(p ^ ~p), em linguagem corrente, é:


João vai ao clube ou hoje é domingo;
João vai ao clube e hoje é domingo;
João nã vai ao clube e hoje não é domingo;
João não vai ao clube e hoje é domingo;
João não vai ao clube ou hoje é domingo.

3. A soma dos elementos da diagonal principal da matriz A =


(aij)3x3, na qual aij = 2i – 3j é igual a:
– 6;
– 4;
– 2;
4;
6.
Capítulo 10

Comparação de algoritmos
Algoritmos estruturados x
orientação a eventos (uma nova
abordagem)
Características principais das linguagens
estruturadas

Quando construímos ou trabalhamos com algoritmos


computacionais voltados para a implementação em uma linguagem
de programação estruturada (como Clipper, C++, Pascal etc.)
sempre visualizamos a fase de implementação como simplesmente
uma “tradução” do algoritmo (pseudocódigo), com suas instruções
em português, para um comando correspondente da linguagem de
programação escolhida (foi o que nos ensinaram!), sendo a única
preocupação a sintaxe correta dos comandos.
Todo aquele arquivo-texto do algoritmo descrito no papel ganha
forma ao ser implementado logicamente como um programa de
computador, sendo, agora, armazenado em um arquivo-fonte, ou
código-fonte, passando a ter, além dos detalhes pertinentes à
linguagem escolhida, um nome com uma extensão exclusiva da
linguagem de programação utilizada para a fase de implementação
(como .prg para o Clipper, .pas para o Pascal, .bas para o Basic,
.cpp para o C++ etc.). Depois de compilado, recebe uma outra
extensão .exe (de executável), estando apto a ser executado pelo
sistema operacional.
Podemos notar, então, em todos estes procedimentos, que o
roteiro seguido por um programador que se utiliza de uma
linguagem de programação estruturada para a construção e
implementação da solução no computador possui sempre a mesma
forma. Todo o algoritmo – com suas áreas de criação de tela, de
interação com o usuário, comandos de leitura, de entrada de dados,
com suas respectivas variáveis e até mesmo os incrementos de
laços de repetição não pertinentes à solução lógica do problema em
questão – é inserido no código-fonte.
A depuração do programa fica ainda mais fácil, pois, a cada erro
encontrado, o número da linha correspondente é exibido e o
programador enxerga sempre todo o seu código-fonte em um lugar
apenas, “facilitando” a manutenção.

Características principais das linguagens


orientadas a objetos e eventos

Vamos partir, agora, da mesma análise feita anteriormente,


mudando apenas a construção e a implementação de algoritmos
voltados das linguagens estruturadas para as linguagens de
programação com orientação a objetos e eventos (como exemplo,
Delphi, da Borland, e Visual Basic, da Microsoft).
Primeiro, gostaria de conceituar alguns itens de suma importância
para leitores leigos neste tipo de linguagem de programação.
Os objetos mencionados serão qualquer componente lógico
predefinido existente na linguagem, o qual terá a função de interagir
com o ambiente externo (usuário ou SO) para a entrada ou saída de
dados. Como exemplo de componentes (objetos), teríamos:
Caixa de texto: objeto que permite a digitação de dados de
entrada;
Rótulo: objeto que permite a exibição das informações sobre os
dados digitados. Ele não é editável;
Botão de comando: objeto que permite, por meio de um click,
processar um código do programa da linguagem escolhida.
Seria o local em que, na maioria das vezes, é inserido o
algoritmo desenvolvido;
Caixa de combinação: objeto que combina uma caixa de texto e
uma lista de dados. Utilizada para que o usuário escolha
determinado valor em uma lista já preenchida. Um exemplo
seria a caixa de combinação que armazena a lista de UFs do
Brasil;
Formulário: objeto que permite realizar a interface com o
usuário (tela). É o local em que inserimos os demais
componentes na construção do programa.
Todos os objetos disponíveis são os mesmos existentes no
ambiente Windows, permitindo, com isso, a criação de um Sistema
de informação com uma aparência bem mais agradável e atrativa
para o usuário.
Um evento nada mais é do que a ação que o usuário exercerá
sobre determinado objeto na execução do programa. Como
exemplo, para a caixa de texto um evento seria:
Ganhar e perder o foco, ao ser pressionada por uma tecla, ao
modificar seu conteúdo etc.;

Já para um formulário:
Ser carregado, ao fechar, ao ativar, ao mover o mouse sobre
ele etc.

Cada objeto possuirá inúmeros eventos associados. Cabe ao


programador, junto ao usuário, definir qual desses eventos terá
função, será utilizado, permanecendo os demais eventos, vazios,
sem codificação, não havendo problema algum nisso.
Choque de estilos:
questionamentos
Vamos, agora, depois de termos visto pequenas particularidades
dos dois estilos de linguagens de programação (estruturada e
orientada a eventos) analisar seriamente o seguinte
questionamento:
Depois de um algoritmo ser escrito em uma forma convencional,
em qual local vou traduzi-lo, se estou trabalhando com uma
linguagem de programação OE?
Primeiro, essa dúvida existe, pois todos nós aprendemos que todo
o texto do algoritmo no papel é transcrito em sua íntegra, para o
arquivo-fonte da linguagem, digitado no próprio computador. Porém,
tratando-se de linguagens OE, isso vai por água baixo, pois muitos
detalhes de implementação diferem da linguagem estruturada, que,
por sua vez, também está definida no pseudocódigo. Como
exemplo, listei alguns itens:
O objeto do tipo formulário será a própria tela de entrada/saída
de dados, evitando, assim, que escrevamos no algoritmo várias
linhas de comando somente para sua montagem;
O comando de entrada de dados LEIA, no algoritmo, também
não faz mais sentido, pois a entrada dos valores pelo usuário,
para o preenchimento das variáveis de memória, é substituída
por objetos do tipo caixa de texto inseridos no próprio
formulário.

Então, como resposta ao questionamento apresentado, teríamos


que existe a dúvida quanto ao local em que serão inseridas as
soluções do problema predefinido, pois, nas linguagens
estruturadas, o pseudocódigo era resolvido do início ao fim em um
mesmo arquivo-fonte; este, nas linguagens OE, aparece todo
desmembrado, sendo inserido nos diversos eventos dos inúmeros
objetos disponíveis na aplicação.
Resolvido esse problema, podemos ir a outro questionamento: a
antiga metodologia de escrita de algoritmos para linguagens
estruturadas atende da mesma forma à solução de problemas
implementados em uma linguagem de programação OE?
Minha opinião acerca deste fato seria negativa, pois já pude
constatar colegas de profissão trabalhando ou lecionando algoritmos
com orientação estruturada nos primeiros anos de cursos
profissionalizantes ou superiores de informática (Pascal) que,
quando avançam para o ano letivo seguinte, cuja linguagem de
programação exigida na grade curricular, para aperfeiçoamento ou
para acompanhar o mercado de desenvolvimento de softwares, é
OE (como Delphi e Visual Basic) abandonam a construção da
solução do problema pela definição de seu algoritmo, partindo direto
para a implementação na máquina (a famosa tentativa e erro!) por
acharem que o estilo de programar exigido pela nova linguagem
difere em muito do ensinado anteriormente, com algoritmos
construídos para linguagens estruturadas.
Eles abandonam a criação do algoritmo por considerarem-no
incapaz de refletir a fase de implementação. Conseqüentemente, o
sistema de informação fica sem uma documentação da solução
algorítmica e o próprio teste da solução ocorre direto no
processamento do programa, o que fere várias regras de construção
e planejamento de soluções e da engenharia de software.
Para contribuir na solução desse impasse, proponho uma nova
metodologia de escrita de algoritmos, metodologia esta que facilitará
uma futura implementação do algoritmo em uma linguagem de
programação OE.
Para começar, veja a estrutura listada:
PROJETO nomedoprojeto

ALGORITMO nomedoalgoritmo
PÚBLICAS
VAR
Nomevar1,Nomevar2 : tipodedado
CONST
Nomeconst : tipodedado

EVENTOS

{A seguir, detalha-se o pseudocódigo para cada componente


utilizado em seus respectivos eventos}
COMPONENTE : nomecomponente
TIPO COMPONENTE : tipocomponente

TIPO EVENTO : nometipoevento


VAR
LOCAL
Nomevar : tipodedado
INÍCIO
BLOCO DE INSTRUÇÕES
FIM

TIPO EVENTO : nometipoevento


[VAR
LOCAL
Nomevar : tipodedado ]
INÍCIO
BLOCO DE INSTRUÇÕES
FIM
{É inserido mais um bloco componente para quantos componentes
forem necessários na solução da aplicação}

COMPONENTE : nomecomponente
TIPO COMPONENTE : tipocomponente

TIPO EVENTO : nometipoevento


VAR
LOCAL
Nomevar : tipodedado
INÍCIO
BLOCO DE INSTRUÇÕES
FIM

FIM ALGORITMO

em que:
PROJETO nomedoprojeto: por trabalhar com o conceito de
projeto, as linguagens OE/OO requerem essa linha, na qual
cada programa de um sistema faz parte de um mesmo projeto.
Essa linha exibe, para facilitar uma futura manutenção ou
documentação, o nome do projeto que está sendo
desenvolvido;
ALGORITMO nomedoalgoritmo: corresponde ao nome do
programa propriamente dito, com o mesmo objetivo do
comando anteriormente utilizado nos algoritmos estruturados;
{ DECLARAÇÕES }
PÚBLICAS
VAR
Nomevar1,Nomevar2 : tipodedado
CONST
Nomeconst : tipodedado

: área que permite as declarações de variáveis globais ou


públicas do algoritmo, ou seja, serão as variáveis que terão
seus valores enxergados por todos os componentes de todo o
formulário ou programa;
COMPONENTE: nomecomponente: descreve o nome do
componente em que vamos detalhar a lógica algorítmica interna
(associada a algum evento);
TIPO COMPONENTE: tipocomponente: descreve o tipo de
componente ou classe a que o componente pertence, como
botão de comando, formulário, caixa de texto, de combinação
etc.;
EVENTOS: comando que permite informar que a área de
descrição da lógica dos eventos será iniciada;
TIPO EVENTO: nometipoevento: informa qual evento associado
ao componente do item quatro será utilizado para codificar a
solução algorítmica;
VAR
LOCAL
Nomevar : tipodedado

: área de declaração de variáveis locais para o evento


associado ao componente escolhido. As variáveis declaradas
nesta área somente são enxergadas naquele evento associado;
INÍCIO

BLOCO DE INSTRUÇÕES

FIM

: área de processamento do algoritmo propriamente dito, onde


se descreve diretamente o bloco de instruções, já que suas
entradas e saídas serão realizadas por componentes
previamente escolhidos pelo programador e desenhados no
formulário.

Observação: a partir de COMPONENTE:


nomecomponente, os itens serão definidos (copiados)
no mesmo pseudocódigo, quantos forem o número de
componentes que precisarem do código de programa
para a solução do problema proposto.

Veja a seguir o exemplo de um algoritmo escrito para uma


linguagem OE que controla o acesso com senha de um usuário ao
menu principal de um sistema qualquer:

Figura 10.1.

Agora, o código:
PROJETO Locadora

ALGORITMO frmCadSenhas

PÚBLICAS
VAR
ContsenhaInvalida : INTEIRO

EVENTOS

COMPONENTE : CmdVerificaSenha
TIPO COMPONENTE : Botão de Comando
TIPO EVENTO : ao Clicar
VAR
LOCAL
Erros : INTEIRO
INÍCIO

SE txtsenha <> “mesa” ENTÃO


ContsenhaInvalida ContsenhaInvalida + 1

SE ContsenhaInvalida > 3 ENTÃO


Mostre “Atenção : Houve mais de três tentativas, o programa
será encerrado, Acesso Negado !”
FIM
FIM-SE

SENÃO
ATIVAR menuPrinc
FIM-SE
FIM

COMPONENTE : FrmCadsenhas
TIPO COMPONENTE : formulário

TIPO EVENTO : Ao Ativar


VAR

INÍCIO

ContsenhaInvalida 0
Txtsenha = “”
CboUsuario FOCO

FIM

COMPONENTE : CmdCancelar
TIPO COMPONENTE : formulário

TIPO EVENTO : Ao Clicar


VAR

INÍCIO

CANCELAR

FIM
FIM ALGORITMO

Observe, então, que o exemplo demonstra como ficaria a solução


do problema solicitado (construção de um controle de acesso com
senha), utilizando a metodologia proposta. Tenho a impressão de
que ela facilitará muito a visualização e o planejamento da solução
do problema, em comparação com o que o que tínhamos antes: a
ida direta ao computador, depois de “desenharmos“ a interface de
entrada para o usuário, para, depois, aplicar a escrita “seca” de
códigos de programas, em que prevalecerá a antiga técnica da
tentativa e erro.
Espero ter contribuído com o aprendizado do leitor ao apresentar
esta nova metodologia. Solicito que quaisquer dúvidas ou sugestões
sejam enviadas a mim.
Experimente, use e abuse esta nova metodologia !
1 2 3

1 X

2 X

3 X

4 X

5 X

6 X

7 X

8 X

9 X

10 X

11 X

12 X
13 X

Tabela 5.1.
x x x x

Tabela 5.2.
x x x x x

Tabela 5.3.
x x x x

Tabela 5.4.

Você também pode gostar