Partilhar: Classroom Teams WhatsApp
aulify · Sebenta
UC · Unidade de Competência · UC00245

Desenvolver algoritmos (UC00245)

Pensamento computacional, pseudocódigo, fluxograma, técnicas de construção e depuração
25h · 2.25 pontos crédito Curso: T. Desenv. Software, T. Multimédia e mais 6 cursos ↗ Referencial oficial SNQ
Índice

Há um pacote premium desta unidade: um PowerPoint editável de 100 ou mais diapositivos, um manual em PDF e dois testes com correção. Esta sebenta continua gratuita.

Introdução

Esta sebenta ensina a pensar antes de programar: como definir um problema, planear a sua solução e construir um algoritmo correto, testado e legível, seja em pseudocódigo ou em fluxograma. É a base de toda a programação, independentemente da linguagem que vieres a aprender a seguir (Python, Java, C, JavaScript…): quem sabe construir um algoritmo bom no papel, aprende qualquer linguagem em poucas semanas.

O percurso segue sempre o mesmo ciclo: definir o problema, planear as etapas, estruturar em pseudocódigo, desenhar em fluxograma, testar e depurar. São as cinco realizações desta UC, e vamos percorrê-las por esta ordem ao longo dos capítulos seguintes.

Objetivos de aprendizagem (referencial): aplicar os princípios do pensamento computacional; definir problemas e planear as suas etapas de resolução; estruturar algoritmos em pseudocódigo e desenhá-los em fluxograma; usar constantes, variáveis e uma primeira estrutura de dados (o vetor), e as estruturas lógicas sequencial, alternativa e repetitiva; aplicar contadores, totalizadores, expressões aritméticas, funções predefinidas e validação de dados; testar, depurar e otimizar algoritmos.

1. Pensamento computacional e definição do problema

O pensamento computacional é a forma de raciocinar sobre um problema de modo a que a solução possa ser executada, passo a passo, por uma pessoa ou por uma máquina. Assenta em quatro princípios:

Antes de desenhar qualquer algoritmo, é preciso definir o problema com rigor. Definir bem um problema significa responder a três perguntas:

  1. Que dados de entrada (inputs) estão disponíveis?
  2. Que resultado (output) se pretende obter?
  3. Que restrições e condições a solução tem de respeitar?

Exemplo resolvido · definir um problema mal formulado

Enunciado vago: "Calcular a média de um aluno."

Esta frase não é um problema bem definido, porque não diz quantas notas, nem de que tipo. Reformulando:

Ler três notas, cada uma entre 0 e 20 valores, e calcular a sua média aritmética, arredondada a uma casa decimal.

Agora já é possível identificar: entrada (três notas reais entre 0 e 20), saída (a média, arredondada), restrição (as notas têm de estar no intervalo 0-20). Só a partir daqui faz sentido planear o algoritmo.

Os quatro princípios na prática

Os princípios do pensamento computacional raramente aparecem sozinhos. Num problema real combinam-se, e vale a pena saber reconhecer cada um quando o usas.

Princípio Pergunta que o ativa Exemplo do dia a dia
Decomposição Que partes mais pequenas tem este problema? Organizar uma viagem: transporte, alojamento, roteiro, orçamento
Reconhecimento de padrões Já resolvi algo parecido? Calcular o troco numa loja e num parque de estacionamento: pago menos custo
Abstração Que informação posso ignorar sem alterar a resposta? Um mapa do metro mostra ligações e estações, não a distância real nem os edifícios
Algoritmia Qual é a sequência exata de passos? Instruções para levantar dinheiro num multibanco

A abstração merece atenção especial. Quando escreves um algoritmo para calcular o preço de uma pizza, o que interessa é o tamanho e os ingredientes extra, não a cor da caixa nem o nome do estafeta. Decidir o que fica de fora é metade do trabalho: um modelo com detalhe a mais é difícil de testar, e um modelo com detalhe a menos dá respostas erradas.

Exemplo resolvido · definir o problema do parque de estacionamento

Enunciado vago: "Calcular quanto se paga no parque."

O que não está dito: como se mede o tempo, qual é o preço, existe um valor máximo diário, o que acontece se o utilizador sair em menos de uns minutos? Para obter um problema bem definido, o professor, o cliente e o programador têm de acordar um enunciado fechado. Suponhamos o seguinte, com valores fictícios para o exercício:

Ler o número de horas completas de estacionamento (inteiro, de 0 a 24). Cada hora custa 1,50 €, mas o valor total nunca ultrapassa 9,00 € por dia. Mostrar o valor a pagar.

Elemento Definição
Entrada horas (inteiro)
Saída valor (real, em euros)
Restrições horas entre 0 e 24; o valor nunca excede 9,00 €
Constantes preço por hora 1.50, limite diário 9.00

Com esta tabela feita, o algoritmo quase se escreve sozinho. Testemos com três valores: 2 horas dão 2 * 1.50 = 3.00 €; 6 horas dão 9.00 €, que é exatamente o limite; 10 horas dariam 15.00 €, mas o teto limita a 9.00 €. Ter calculado estes casos antes de escrever o pseudocódigo permite, mais tarde, saber se o algoritmo está certo.

Exemplo resolvido · reconhecer um padrão

Considera estes três enunciados: (1) somar o preço de todos os artigos de um carrinho; (2) somar as horas trabalhadas em cada dia de uma semana; (3) somar as notas de uma turma para calcular a média. À superfície são problemas diferentes, mas o padrão é o mesmo: partir de zero, percorrer uma lista de valores e acumular a soma. Reconhecido o padrão, escreve-se um único esquema de algoritmo (um totalizador dentro de um ciclo, que vemos no capítulo 11) e aplica-se às três situações, trocando apenas o nome das variáveis. É este reaproveitamento que faz um programador tornar-se mais rápido com a experiência.

2. Planear as etapas de um algoritmo

Definido o problema, o passo seguinte é planear a solução: decompor em subproblemas e ordená-los.

Exemplo resolvido · decompor um problema

Problema: calcular o valor a pagar numa loja, com desconto para clientes fidelizados.

Decomposição em subproblemas:

  1. Ler o valor total da compra.
  2. Ler se o cliente é fidelizado (sim/não).
  3. Se for fidelizado, calcular o desconto (10%; percentagem fictícia, só para o exercício).
  4. Calcular o valor final a pagar.
  5. Mostrar o resultado ao operador de caixa.

Cada subproblema corresponde a uma ou poucas linhas do algoritmo final. Planear desta forma evita esquecer passos e ajuda a identificar, desde já, que o subproblema 3 vai precisar de uma estrutura alternativa (capítulo 7).

Técnica de refinamentos sucessivos

Uma forma prática de decompor é partir de um enunciado geral e ir refinando em níveis. Cada nível detalha o anterior, até que cada passo seja tão simples que se escreva numa única linha de pseudocódigo. Chama-se abordagem de cima para baixo (top-down).

Um bom plano cumpre critérios simples:

Critério Pergunta de verificação
Completo Se executar todos os passos pela ordem, obtenho o resultado pretendido?
Ordenado Cada passo tem à disposição os dados de que precisa?
Sem sobreposição Algum passo repete o trabalho de outro?
Testável Consigo verificar o resultado de cada passo isoladamente?
Proporcionado Os passos têm um tamanho semelhante, sem um gigante rodeado de minúsculos?

Exemplo resolvido · decompor o tratamento das notas de uma turma

Problema: dadas as notas de 5 alunos, mostrar a média da turma, a nota mais alta e quantos alunos estão aprovados (nota igual ou superior a 10).

Nível 0: tratar as notas da turma.

Nível 1: 1. Ler as 5 notas. 2. Calcular a média. 3. Descobrir a nota mais alta. 4. Contar os aprovados. 5. Mostrar os três resultados.

Nível 2 (refinar os passos 1 a 4, que escondem ciclos): - 1.1 Repetir 5 vezes: pedir e ler uma nota. - 2.1 Somar cada nota lida a um total, que começa em 0. - 2.2 No fim, dividir o total por 5. - 3.1 Guardar a primeira nota como "maior até agora". - 3.2 A cada nota seguinte, se for maior do que a guardada, substituir. - 4.1 Começar um contador a 0 e somar 1 sempre que a nota for maior ou igual a 10.

Repara que os passos 1, 2, 3 e 4 podem partilhar um só ciclo: ler uma nota, somá-la ao total, comparar com o máximo e contar se aprovada. A decomposição serve para pensar com clareza; só depois decidimos como a juntar de forma eficiente. A solução completa deste problema aparece no capítulo 11.

Exemplo resolvido · da decomposição ao plano de testes

Ainda antes de escrever pseudocódigo, o plano pode incluir o que vamos testar. Para o problema acima, antecipamos as notas 12, 8, 15, 10 e 18. Calculamos à mão: o total é 63; a média é 12,6; a nota mais alta é 18; aprovados são 12, 15, 10 e 18, ou seja, 4. Este "gabarito" é o termo de comparação quando o algoritmo estiver escrito.

3. O que é um algoritmo: ação, estado e propriedades

Um algoritmo é uma sequência finita, ordenada e não ambígua de ações que resolve um problema. Cada ação transforma o estado do sistema: muda o valor de uma variável, lê um dado ou escreve um resultado. O estado da ação é a "fotografia" dos valores de todas as variáveis num dado momento da execução.

Todo o algoritmo cumpre quatro propriedades:

Propriedade Significa
Finito tem de terminar ao fim de um número finito de passos
Definido cada passo é claro, sem ambiguidade
Determinístico a mesma entrada produz sempre a mesma saída
Eficaz cada passo é realmente executável

O desenvolvimento de um algoritmo segue sempre o mesmo ciclo, que estrutura o resto desta sebenta:

Definir o problema → Planear as etapas → Estruturar em pseudocódigo
→ Desenhar em fluxograma → Testar e depurar

Ação e estado: seguir o algoritmo passo a passo

Cada ação muda o estado, e é por isso que nos conseguimos convencer de que um algoritmo funciona: seguimos o estado de passo em passo. Escrevemos o estado como uma pequena tabela com uma linha por ação executada.

Exemplo resolvido · trocar o conteúdo de duas variáveis

Problema: a vale 5 e b vale 8. Trocar os valores, de modo a que a fique com 8 e b com 5.

Tentativa ingénua:

a ← b
b ← a
Ação executada a b
Estado inicial 5 8
a ← b 8 8
b ← a 8 8

O primeiro valor de a perdeu-se antes de ser copiado. Solução: uma variável auxiliar que guarda temporariamente o valor.

aux ← a
a ← b
b ← aux
Ação executada a b aux
Estado inicial 5 8 (nenhuma)
aux ← a 5 8 5
a ← b 8 8 5
b ← aux 8 5 5

Este pequeno exemplo mostra duas lições. A ordem das ações é parte do significado do algoritmo, e uma atribuição substitui o valor anterior de forma irreversível.

As propriedades, com contraexemplos

Perceber uma propriedade é saber reconhecer quando falha.

Propriedade Contraexemplo Porquê falha
Finito "Repetir enquanto 1 for igual a 1" nunca termina
Definido "Juntar sal q.b." "q.b." depende de quem executa
Determinístico "Escolher um número qualquer" a mesma entrada não garante a mesma saída
Eficaz "Calcular a raiz quadrada de 2 com todas as casas decimais" nenhuma máquina executa um número infinito de passos

Os ciclos infinitos são o exemplo mais frequente de algoritmos que não são finitos e, na prática, bloqueiam o computador.

As etapas de desenvolvimento, uma a uma

Etapa Pergunta que responde Produto
Definir o problema O que entra, o que sai, que regras há? Enunciado fechado
Planear Em que partes se divide e por que ordem? Lista de subproblemas
Estruturar em pseudocódigo Como escrevo cada passo de forma exata? Pseudocódigo
Desenhar em fluxograma Consigo ver o fluxo e os desvios? Fluxograma
Testar e depurar Funciona com dados típicos, limite e inválidos? Trace tables e correções

O ciclo raramente é linear: ao testar descobre-se muitas vezes que o problema estava mal definido, e volta-se atrás. Isso não é falhar, é o processo normal de desenvolvimento.

4. Tipos de dados: constantes e variáveis

Um algoritmo manipula dados, guardados em constantes e variáveis:

Toda a variável deve ser inicializada antes de ser usada; usar uma variável sem valor definido é um dos erros mais frequentes desta matéria. Nomes descritivos (preco_total, não x) tornam o algoritmo legível.

Cada dado tem um tipo, que determina as operações válidas:

Tipo Exemplos Operações típicas
Inteiro 5, -3, 120 + - * DIV MOD
Real 3.14, -0.5 + - * /
Texto (cadeia) "Aulify" concatenação
Lógico (booleano) Verdadeiro, Falso E, OU, NÃO

Escolher o tipo certo evita erros: 7 DIV 2 = 3 (divisão inteira), mas 7 / 2 = 3.5 (divisão real).

Nomes, tipos e conversões

Os nomes de variáveis seguem regras simples nesta sebenta: começam por uma letra, usam letras minúsculas, algarismos e _, sem espaços, e descrevem o conteúdo (preco_unitario, numAlunos). Evitamos acentos nos nomes para facilitar a transição para as linguagens de programação.

A escolha do tipo depende do que se faz com o dado, não apenas do seu aspeto:

Dado Tipo adequado Razão
Idade Inteiro contam-se anos completos
Preço Real tem cêntimos
Quantidade de artigos Inteiro não existem 2,5 canetas
Número de telefone Texto não se somam telefones, e pode começar por zero ou por +
Código postal Texto contém um hífen e zeros à esquerda
Aprovado? Lógico só tem dois valores possíveis

Quando um número é guardado como texto, as operações aritméticas deixam de fazer sentido; quando um código é guardado como número, perdem-se os zeros à esquerda. Em ambos os casos o tipo errado produz um erro silencioso.

O texto tem operações próprias. A concatenação junta cadeias ("Olá, " + nome) e a função COMPRIMENTO(nome) devolve quantos carateres tem. O tipo lógico combina-se com E, OU e NÃO, e é o resultado natural de qualquer comparação.

Exemplo resolvido · declarar os dados de uma compra

Problema: guardar o nome do produto, o preço unitário, a quantidade e se há promoção.

Nome Tipo Constante ou variável Exemplo
produto Texto variável "Rato sem fios"
preco_unitario Real variável 12.50
quantidade Inteiro variável 3
ha_promocao Lógico variável Verdadeiro
TAXA_PROMO Real constante 0.10

Com estes dados, total ← preco_unitario * quantidade dá 37.50, e SE ha_promocao ENTÃO total ← total - total * TAXA_PROMO reduz-o para 33.75. Conferindo as contas: 12.50 * 3 = 37.50; 37.50 * 0.10 = 3.75; 37.50 - 3.75 = 33.75. Preço e taxa fictícios, só para o exercício.

Repara que a taxa de promoção é uma constante com nome. Se escrevesses 0.10 em vários sítios do algoritmo e depois a taxa mudasse, terias de encontrar todas as ocorrências; com a constante, mudas uma única linha.

Atribuição versus igualdade

Duas ideias que confundem os principiantes. A atribuição x ← x + 1 não é uma equação matemática (que seria impossível): lê-se "calcula o valor atual de x mais 1 e guarda o resultado em x". Já a comparação x = 5 pergunta se x vale 5 e devolve Verdadeiro ou Falso, sem mudar nada.

Estruturas de dados: o vetor

Até aqui, cada variável guardava um único valor. Quando há vários valores do mesmo tipo e do mesmo significado (as notas de uma turma, os preços de uma encomenda), cinco variáveis soltas tornam-se pouco práticas. Uma estrutura de dados resolve isso: organiza vários valores sob um só nome. A mais simples é o vetor, que guarda vários valores do mesmo tipo, cada um identificado por um índice (nesta sebenta, a começar em 1). notas[1] é a primeira nota e notas[5] a quinta.

notas: vetor de 5 reais
total ← 0
PARA i DE 1 ATÉ 5 FAÇA
   LER notas[i]
   total ← total + notas[i]
FIM PARA
ESCREVER "Média: ", total / 5

Com as notas 12, 8, 15, 10 e 18 o vetor fica [12, 8, 15, 10, 18], o total é 63 e a média é 12.6. A vantagem sobre cinco variáveis soltas é poder percorrer todos os valores com um ciclo PARA (que estudamos no capítulo 8) e guardá-los para os consultar depois: terminada a leitura, notas[3] continua a valer 15. O índice tem de estar entre 1 e o tamanho do vetor; notas[6] não existe e seria um erro.

5. Entrada e saída de dados

Todo o algoritmo interage com o exterior através de duas operações elementares: LER e ESCREVER.

INICIO
   ESCREVER "Introduza o seu nome:"
   LER nome
   ESCREVER "Olá, ", nome
FIM

Boas práticas:

Mais exemplos de entrada e saída

Uma interação bem desenhada guia o utilizador e devolve um resultado completo.

Exemplo resolvido · soma e produto de dois números

INICIO
   ESCREVER "Primeiro número:"
   LER a
   ESCREVER "Segundo número:"
   LER b
   soma ← a + b
   produto ← a * b
   ESCREVER "Soma: ", soma
   ESCREVER "Produto: ", produto
FIM

Se o utilizador escrever 6 e 7, o ecrã mostra Soma: 13 e Produto: 42. Repara na estrutura de três blocos que se repete em quase todos os algoritmos: entrada, processamento, saída.

Quando um dado é lido várias vezes

Para ler, por exemplo, três notas, não é preciso escrever três vezes o mesmo bloco de pedido e leitura se usarmos um ciclo (capítulo 8). Mas fora de ciclos, é aceitável ler variáveis distintas, uma por dado, sempre com uma mensagem própria para cada uma. Uma mensagem pouco específica ("Introduza um valor:") confunde o utilizador quando há vários valores a pedir.

6. Estrutura sequencial

A estrutura sequencial é a mais simples das três estruturas lógicas: as ações executam-se uma a seguir à outra, pela ordem em que foram escritas.

Exemplo resolvido · área de um retângulo

INICIO
   LER base
   LER altura
   area ← base * altura
   ESCREVER "Área: ", area
FIM

Não há decisões nem repetições, só uma linha reta de execução. A ordem das instruções não é arbitrária: uma variável só pode ser usada depois de ter sido definida, por isso base e altura são lidas antes de area ser calculada. A estrutura sequencial é a base sobre a qual se constroem as duas estruturas seguintes.

Mais exemplos de estrutura sequencial

Exemplo resolvido · converter graus Celsius em Fahrenheit

A fórmula é F = C * 9 / 5 + 32.

INICIO
   ESCREVER "Temperatura em Celsius:"
   LER celsius
   fahrenheit ← celsius * 9 / 5 + 32
   ESCREVER celsius, " °C = ", fahrenheit, " °F"
FIM

Testes, feitos à mão antes de executar: 0 °C dá 32 °F; 100 °C dá 212 °F; 37 °C dá 98.6 °F. O algoritmo é puramente sequencial, e a ordem de cálculo respeita a precedência das operações: multiplica e divide primeiro, soma depois.

Exemplo resolvido · converter segundos em horas, minutos e segundos

Problema: dado um número de segundos, mostrar a duração no formato horas, minutos e segundos.

Este é um bom problema para praticar DIV e MOD.

INICIO
   LER total
   horas ← total DIV 3600
   resto ← total MOD 3600
   minutos ← resto DIV 60
   segundos ← resto MOD 60
   ESCREVER horas, "h ", minutos, "min ", segundos, "s"
FIM

Trace para total = 3725:

Ação horas resto minutos segundos
LER total (3725) (nenhuma) (nenhuma) (nenhuma) (nenhuma)
horas ← 3725 DIV 3600 1 (nenhuma) (nenhuma) (nenhuma)
resto ← 3725 MOD 3600 1 125 (nenhuma) (nenhuma)
minutos ← 125 DIV 60 1 125 2 (nenhuma)
segundos ← 125 MOD 60 1 125 2 5

O resultado é 1h 2min 5s. Confirma: 1 * 3600 + 2 * 60 + 5 = 3725. Esta contraprova, fazer a operação inversa, é uma ótima forma de testar.

O erro da ordem trocada

Um erro frequente nas sequências é usar uma variável antes de lhe dar valor. Se, no exemplo acima, a linha minutos ← resto DIV 60 viesse antes de resto ← total MOD 3600, o algoritmo usaria um resto que ainda não existe. Num algoritmo sequencial, cada linha só pode depender do que está acima.

7. Estrutura alternativa

A estrutura alternativa permite escolher entre caminhos diferentes consoante uma condição, que só pode ser Verdadeira ou Falsa.

SE simples:

SE nota >= 10 ENTÃO
   ESCREVER "Aprovado"
FIM SE

SE composto (com SENÃO, para o caso em que a condição é falsa):

SE nota >= 10 ENTÃO
   ESCREVER "Aprovado"
SENÃO
   ESCREVER "Reprovado"
FIM SE

SE encadeado, quando há mais de duas hipóteses:

SE nota >= 18 ENTÃO
   ESCREVER "Excelente"
SENÃO SE nota >= 10 ENTÃO
   ESCREVER "Aprovado"
SENÃO
   ESCREVER "Reprovado"
FIM SE

As condições são testadas por ordem, de cima para baixo; assim que uma é verdadeira, as restantes deixam de ser testadas. Regra prática: nunca mais do que 2 ou 3 níveis de aninhamento, ou o algoritmo torna-se ilegível.

Exemplo resolvido · desconto por categoria

Problema: ler o preço de uma compra e a categoria do cliente ("Premium" ou "Regular"), aplicando 15% de desconto a clientes Premium (sempre) e 5% a clientes Regular apenas quando a compra ultrapassa 50 €. Percentagens e valores fictícios, só para o exercício.

INICIO
   LER preco
   LER categoria
   SE categoria = "Premium" ENTÃO
      desconto ← preco * 0.15
   SENÃO SE categoria = "Regular" E preco > 50 ENTÃO
      desconto ← preco * 0.05
   SENÃO
      desconto ← 0
   FIM SE
   total ← preco - desconto
   ESCREVER "Total a pagar: ", total
FIM

Condições compostas e tabelas de verdade

Uma condição pode juntar várias comparações com os operadores lógicos. A tabela de verdade diz o resultado de cada combinação.

A B A E B A OU B NÃO A
Verdadeiro Verdadeiro Verdadeiro Verdadeiro Falso
Verdadeiro Falso Falso Verdadeiro Falso
Falso Verdadeiro Falso Verdadeiro Verdadeiro
Falso Falso Falso Falso Verdadeiro

Em palavras: E só é verdadeiro quando ambas as condições o são; OU só é falso quando ambas o são; NÃO inverte o valor. Quando misturas E e OU na mesma condição, usa parênteses para tornar a intenção explícita: SE (idade >= 18 E temCartao) OU eFuncionario ENTÃO.

Uma regra útil para simplificar condições negadas (as leis de De Morgan): NÃO (A E B) equivale a (NÃO A) OU (NÃO B), e NÃO (A OU B) equivale a (NÃO A) E (NÃO B). Por exemplo, "não é (maior de idade e com cartão)" é o mesmo que "é menor de idade ou não tem cartão".

Exemplo resolvido · par ou ímpar

Um número é par se o resto da divisão por 2 for zero.

INICIO
   LER n
   SE n MOD 2 = 0 ENTÃO
      ESCREVER n, " é par"
   SENÃO
      ESCREVER n, " é ímpar"
   FIM SE
FIM

Testes: 10 MOD 2 = 0 dá par; 7 MOD 2 = 1 dá ímpar; 0 MOD 2 = 0 dá par (o zero é par). O caso do zero é típico de valor-limite, e mostra que o algoritmo está correto sem exceções.

Exemplo resolvido · a ordem das condições num SE encadeado

Considera uma classificação com quatro escalões: 0 a 9 "Insuficiente", 10 a 13 "Suficiente", 14 a 17 "Bom", 18 a 20 "Excelente". Um aluno escreve:

SE nota >= 10 ENTÃO
   ESCREVER "Suficiente"
SENÃO SE nota >= 14 ENTÃO
   ESCREVER "Bom"
SENÃO SE nota >= 18 ENTÃO
   ESCREVER "Excelente"
SENÃO
   ESCREVER "Insuficiente"
FIM SE

Com nota = 19 o algoritmo escreve "Suficiente", porque 19 >= 10 é verdadeiro e as restantes condições nunca chegam a ser testadas. A correção é testar do caso mais exigente para o menos exigente:

SE nota >= 18 ENTÃO
   ESCREVER "Excelente"
SENÃO SE nota >= 14 ENTÃO
   ESCREVER "Bom"
SENÃO SE nota >= 10 ENTÃO
   ESCREVER "Suficiente"
SENÃO
   ESCREVER "Insuficiente"
FIM SE

Testes com valores nas fronteiras: 9 dá Insuficiente, 10 dá Suficiente, 13 dá Suficiente, 14 dá Bom, 17 dá Bom, 18 dá Excelente, 20 dá Excelente. As fronteiras são onde os erros se escondem.

Exemplo resolvido · ano bissexto

Um ano é bissexto se for divisível por 4, exceto se for divisível por 100, a não ser que também seja divisível por 400.

INICIO
   LER ano
   SE (ano MOD 4 = 0 E ano MOD 100 <> 0) OU ano MOD 400 = 0 ENTÃO
      ESCREVER ano, " é bissexto"
   SENÃO
      ESCREVER ano, " não é bissexto"
   FIM SE
FIM
ano MOD 4 = 0 MOD 100 <> 0 MOD 400 = 0 Resultado
2024 Verdadeiro Verdadeiro Falso bissexto
1900 Verdadeiro Falso Falso não bissexto
2000 Verdadeiro Falso Verdadeiro bissexto
2023 Falso Verdadeiro Falso não bissexto

Esta tabela é uma forma excelente de testar condições compostas: uma linha por caso e uma coluna por cada parte da condição.

Exemplo resolvido · o maior de três números

INICIO
   LER a
   LER b
   LER c
   maior ← a
   SE b > maior ENTÃO
      maior ← b
   FIM SE
   SE c > maior ENTÃO
      maior ← c
   FIM SE
   ESCREVER "O maior é ", maior
FIM

Em vez de compararmos todos com todos (que exigiria muitos E e vários ramos), partimos do pressuposto de que o primeiro é o maior e corrigimos à medida que encontramos um valor maior. Para a = 4, b = 9, c = 7: maior começa em 4, passa a 9 no primeiro SE e mantém-se em 9 no segundo. Este esquema repete-se com listas de qualquer tamanho (capítulo 11).

8. Estruturas repetitivas

Uma estrutura repetitiva (ciclo) executa um bloco de instruções várias vezes. Há três formas, cada uma adequada a uma situação diferente.

PARA: número de repetições conhecido

PARA i DE 1 ATÉ 10 FAÇA
   ESCREVER i
FIM PARA

i é a variável de controlo, atualizada automaticamente a cada iteração. DE ... ATÉ ... define o intervalo; PASSO (opcional) define o incremento.

ENQUANTO: pré-teste, número de repetições desconhecido

LER num
ENQUANTO num <> 0 FAÇA
   ESCREVER "Recebi: ", num
   LER num
FIM ENQUANTO

A condição é testada antes de cada iteração; se já for falsa à partida, o corpo nunca executa.

REPETIR ATÉ: pós-teste, executa sempre pelo menos uma vez

REPETIR
   ESCREVER "Introduza a nota (0-20):"
   LER nota
ATÉ nota >= 0 E nota <= 20
ENQUANTO REPETIR ATÉ
Teste antes (pré-teste) depois (pós-teste)
Executa pelo menos uma vez não garante sim, sempre
Uso típico leitura com sentinela validação de entrada

Exemplo resolvido · trace table de um ciclo PARA

Para o algoritmo soma ← 0; PARA i DE 1 ATÉ 5 FAÇA soma ← soma + i FIM PARA:

Iteração i soma (depois)
Inicial (nenhuma) 0
1 1 1
2 2 3
3 3 6
4 4 10
5 5 15

A cada iteração, a tabela regista o novo valor da variável de controlo e do acumulador. É a ferramenta central para testar ciclos (capítulo 12).

Ciclos aninhados

Um ciclo pode conter outro ciclo. O ciclo interior executa-se por completo, do início ao fim, a cada iteração do ciclo exterior.

Exemplo resolvido · tabuadas do 1 ao 3

PARA i DE 1 ATÉ 3 FAÇA
   PARA j DE 1 ATÉ 3 FAÇA
      ESCREVER i, " x ", j, " = ", i * j
   FIM PARA
FIM PARA
i j Escreve
1 1 1 x 1 = 1
1 2 1 x 2 = 2
1 3 1 x 3 = 3
2 1 2 x 1 = 2
2 2 2 x 2 = 4
2 3 2 x 3 = 6
3 1 3 x 1 = 3
3 2 3 x 2 = 6
3 3 3 x 3 = 9

O corpo interior executa 3 * 3 = 9 vezes. Em geral, se o exterior repete m vezes e o interior n, o corpo interior corre m * n vezes. Isto importa para a otimização: ciclos aninhados crescem depressa.

PARA com passo e contagem decrescente

O PASSO altera o incremento. Um passo negativo conta para trás.

PARA i DE 10 ATÉ 1 PASSO -1 FAÇA
   ESCREVER i
FIM PARA
ESCREVER "Fogo!"

Escreve 10, 9, 8, ..., 1 e depois "Fogo!". Outro uso comum é percorrer os pares: PARA i DE 2 ATÉ 10 PASSO 2 visita 2, 4, 6, 8 e 10. A soma destes cinco valores é 30.

Exemplo resolvido · soma dos algarismos de um número com ENQUANTO

Problema: dado um inteiro positivo, somar os seus algarismos. Aqui não sabemos à partida quantos algarismos tem, por isso PARA não serve.

INICIO
   LER n
   soma ← 0
   ENQUANTO n > 0 FAÇA
      soma ← soma + n MOD 10
      n ← n DIV 10
   FIM ENQUANTO
   ESCREVER "Soma dos algarismos: ", soma
FIM

Ideia: n MOD 10 isola o último algarismo e n DIV 10 retira-o. Trace para n = 1234:

Iteração n (no teste) último algarismo soma (depois) n (depois)
1 1234 4 4 123
2 123 3 7 12
3 12 2 9 1
4 1 1 10 0
Teste 0 (nenhuma) 10 0

Com n = 0 na quinta verificação a condição n > 0 é falsa e o ciclo termina. O resultado é 10. Repara que este ciclo termina sempre, porque n diminui a cada passagem e acaba por chegar a 0.

Exemplo resolvido · número primo

Um número é primo se for maior do que 1 e só tiver dois divisores: 1 e ele próprio. Para decidir, tentamos dividir por todos os valores de 2 até n - 1.

INICIO
   LER n
   primo ← Verdadeiro
   SE n < 2 ENTÃO
      primo ← Falso
   FIM SE
   d ← 2
   ENQUANTO d < n E primo FAÇA
      SE n MOD d = 0 ENTÃO
         primo ← Falso
      FIM SE
      d ← d + 1
   FIM ENQUANTO
   SE primo ENTÃO
      ESCREVER n, " é primo"
   SENÃO
      ESCREVER n, " não é primo"
   FIM SE
FIM

Variável lógica primo funciona como uma bandeira (flag): parte de Verdadeiro e baixa a Falso quando se descobre um divisor. Trace para n = 9:

d 9 MOD d primo (depois)
2 1 Verdadeiro
3 0 Falso

Como primo ficou Falso, a condição d < n E primo deixa de ser verdadeira e o ciclo para. Resultado: 9 não é primo. Para n = 7 o ciclo percorre d de 2 a 6 sem encontrar divisor, e 7 é primo. Para n = 1 e n = 2 o ciclo nem chega a executar (no 1 por ser menor que 2; no 2 porque d < n é falso logo à partida), e o resultado é o correto: 1 não é primo, 2 é primo.

Converter entre tipos de ciclo

Qualquer PARA pode escrever-se como ENQUANTO: inicializa-se a variável de controlo antes, testa-se a condição e incrementa-se no fim do corpo.

i ← 1
ENQUANTO i <= 5 FAÇA
   ESCREVER i
   i ← i + 1
FIM ENQUANTO

faz o mesmo que PARA i DE 1 ATÉ 5 FAÇA ESCREVER i FIM PARA. O inverso nem sempre é natural, e é por isso que escolhemos o ciclo que exprime melhor a intenção: PARA quando o número de repetições é conhecido, ENQUANTO quando depende de uma condição que pode falhar logo à primeira, REPETIR ATÉ quando o corpo tem de correr pelo menos uma vez.

Exemplo resolvido · menu com REPETIR ATÉ

REPETIR
   ESCREVER "1 - Somar   2 - Multiplicar   0 - Sair"
   LER opcao
   SE opcao = 1 ENTÃO
      ESCREVER "A somar..."
   SENÃO SE opcao = 2 ENTÃO
      ESCREVER "A multiplicar..."
   FIM SE
ATÉ opcao = 0

O menu tem de aparecer pelo menos uma vez para que o utilizador possa escolher, o que torna o REPETIR ATÉ a escolha natural. Se o utilizador escrever 5, nenhum ramo é executado e o menu volta a aparecer.

9. Pseudocódigo

O pseudocódigo é uma notação intermédia entre a linguagem natural e uma linguagem de programação: estruturada, mas sem a sintaxe rígida de uma linguagem em concreto. Convenções desta sebenta:

Boas práticas de escrita:

O pseudocódigo é transferível para qualquer linguagem: quem sabe pensar em pseudocódigo aprende Python, Java ou C em poucas semanas.

Do pseudocódigo ao código

O pseudocódigo é independente da linguagem, mas a tradução para uma linguagem real é direta. Vê como o mesmo algoritmo se escreve em pseudocódigo e em Python:

INICIO
   total ← 0
   PARA i DE 1 ATÉ 5 FAÇA
      LER preco
      total ← total + preco
   FIM PARA
   ESCREVER "Total: ", total
FIM
total = 0
for i in range(1, 6):
    preco = float(input("Preço: "))
    total = total + preco
print("Total:", total)

Correspondência entre os dois mundos:

Pseudocódigo Python
← =
= (comparar) ==
<> !=
E, OU, NÃO and, or, not
PARA i DE 1 ATÉ 5 for i in range(1, 6)
ENQUANTO cond FAÇA while cond:
DIV, MOD //, %

Em Python, range(1, 6) termina antes de 6, o que é uma das diferenças que apanham os principiantes. A estrutura lógica é a mesma, só a sintaxe muda: por isso se diz que quem domina o pseudocódigo aprende uma linguagem depressa.

Exemplo resolvido · bom e mau pseudocódigo

Mau:

LER x
LER y
z ← x * y / 2
ESCREVER z

Bom:

INICIO
   ESCREVER "Base do triângulo:"
   LER base
   ESCREVER "Altura do triângulo:"
   LER altura
   area ← base * altura / 2
   ESCREVER "Área: ", area
FIM

Os dois fazem o mesmo, mas o segundo explica-se sozinho: quem o lê percebe que calcula a área de um triângulo, sem precisar de adivinhar o que são x, y e z. Com base = 6 e altura = 4, a área é 12.

10. Fluxograma

O fluxograma representa o algoritmo com símbolos gráficos normalizados (norma ISO 5807:1985), ligados por setas que indicam o fluxo de execução.

Símbolo Nome Representa
Retângulo de cantos arredondados (muitas aplicações usam uma elipse) Terminal início ou fim
Paralelogramo Entrada/Saída LER, ESCREVER
Retângulo Processo cálculo, atribuição
Losango Decisão condição (SE)
Seta Fluxo ordem de execução

Regras de boa prática (não são exigências da norma):

Exemplo resolvido · converter pseudocódigo em fluxograma

Para o pseudocódigo:

INICIO
   LER nota
   SE nota >= 10 ENTÃO
      ESCREVER "Aprovado"
   SENÃO
      ESCREVER "Reprovado"
   FIM SE
FIM

O fluxograma equivalente é: Terminal «INÍCIO» → Paralelogramo «LER nota» → Losango «nota ≥ 10?» com duas saídas (V leva a Paralelogramo «ESCREVER Aprovado»; F leva a Paralelogramo «ESCREVER Reprovado») → as duas saídas convergem → Terminal «FIM». Fluxograma e pseudocódigo são duas representações do mesmo algoritmo: o que se pode escrever num, pode desenhar-se no outro. Aplicações de desenho de algoritmos (draw.io, Lucidchart, Flowgorithm) ajudam a manter a norma.

Representar ciclos no fluxograma

Os ciclos desenham-se com um losango de decisão cuja saída "Falso" ou "Verdadeiro" volta atrás por uma seta, formando um circuito. Para o ENQUANTO, o losango fica no topo do circuito (o teste é feito antes do corpo). Para o REPETIR ATÉ, o losango fica no fim (o teste é feito depois do corpo).

Exemplo resolvido · fluxograma de um ciclo com contador

Pseudocódigo:

INICIO
   i ← 1
   ENQUANTO i <= 3 FAÇA
      ESCREVER i
      i ← i + 1
   FIM ENQUANTO
FIM

Fluxograma, lido seta a seta:

  1. Terminal «INÍCIO».
  2. Retângulo «i ← 1».
  3. Losango «i ≤ 3?». Se Verdadeiro, segue para o passo 4. Se Falso, segue para o passo 6.
  4. Paralelogramo «ESCREVER i».
  5. Retângulo «i ← i + 1», com seta de volta ao losango do passo 3.
  6. Terminal «FIM».

Percorrendo o desenho com i = 1, 2 e 3, o algoritmo escreve 1, 2 e 3; com i = 4 o losango dá Falso e o fluxo sai do circuito. Se esquecesses o retângulo do passo 5, o desenho mostraria logo o problema: a seta de retorno passaria por um ponto onde nada altera i, e o circuito nunca terminaria. É por isso que o fluxograma é uma boa ferramenta para detetar ciclos infinitos.

Método para passar do pseudocódigo ao fluxograma

Passo O que fazer
1 Desenha o terminal de início
2 Para cada LER ou ESCREVER, um paralelogramo
3 Para cada atribuição ou cálculo, um retângulo
4 Para cada SE, um losango com duas saídas rotuladas, que se voltam a juntar no fim da estrutura
5 Para cada ciclo, um losango com seta de retorno
6 Termina com o terminal de fim, onde todos os caminhos convergem

Erros frequentes nos fluxogramas

Erro Porquê é um problema
Losango com uma só saída uma decisão tem sempre dois resultados
Setas sem rótulo nas decisões não se sabe qual é o ramo Verdadeiro
Caminhos que não chegam ao terminal final o algoritmo parece nunca terminar
Texto longo dentro dos símbolos perde-se a leitura rápida; escreve a ação, não uma frase
Retângulo usado para leitura de dados os dados lidos ou escritos usam o paralelogramo

Na prática, as aplicações de desenho de algoritmos permitem mover símbolos, alinhar setas e exportar a imagem. Algumas, como o Flowgorithm, executam o fluxograma passo a passo, o que equivale a uma trace table automática. Escolhe a ferramenta que a turma tiver disponível: o importante é a lógica, não o programa.

11. Técnicas de construção: contadores, totalizadores, expressões e funções predefinidas

Contadores e totalizadores

Um contador regista quantas vezes algo acontece, somando sempre a mesma quantidade fixa (tipicamente 1):

contador ← 0
PARA i DE 1 ATÉ n FAÇA
   LER nota
   SE nota >= 10 ENTÃO
      contador ← contador + 1
   FIM SE
FIM PARA

Um totalizador (ou acumulador) soma valores variáveis, lidos ou calculados a cada iteração:

total ← 0
PARA i DE 1 ATÉ n FAÇA
   LER preco
   total ← total + preco
FIM PARA

Inicializa-se um acumulador-soma a 0, e um acumulador-produto a 1 (por exemplo, no cálculo de um fatorial); inicializar mal é a causa mais comum de resultados errados neste padrão.

Expressões aritméticas, relacionais e lógicas

Tipo Operadores Resultado
Aritmética + - * / DIV MOD ^ um número
Relacional = <> < > <= >= Verdadeiro/Falso
Lógica E, OU, NÃO Verdadeiro/Falso

DIV devolve a parte inteira da divisão, MOD devolve o resto: 7 DIV 2 = 3, 7 MOD 2 = 1. As expressões lógicas combinam várias condições, como em SE idade >= 18 E temCartao ENTÃO.

Funções predefinidas

Função Faz
RAIZ(x) raiz quadrada de x
ABS(x) valor absoluto de x
ARREDONDAR(x) arredonda x ao inteiro mais próximo
MAIOR(a, b) / MENOR(a, b) maior ou menor de dois valores
COMPRIMENTO(cadeia) número de carateres de uma cadeia de texto

Usar uma função predefinida evita reescrever, com risco de erro, uma operação que já existe.

Exemplo resolvido · fatorial com acumulador-produto

INICIO
   LER n
   fact ← 1
   PARA i DE 1 ATÉ n FAÇA
      fact ← fact * i
   FIM PARA
   ESCREVER n, "! = ", fact
FIM

Para n = 0, o ciclo não executa e o resultado fica 1 (0! = 1, por definição), o que é correto graças à inicialização fact ← 1.

Padrões clássicos: média, máximo, mínimo e sentinela

Muitos problemas resolvem-se combinando poucos esquemas. Vale a pena conhecê-los pelo nome.

Padrão Para que serve Inicialização típica
Contador quantos casos contador ← 0
Totalizador soma de valores total ← 0
Acumulador-produto produto de valores produto ← 1
Máximo maior valor maior ← primeiro valor lido
Mínimo menor valor menor ← primeiro valor lido
Média total dividido por contador calcula-se no fim, depois do ciclo
Sentinela valor especial que termina a leitura lê-se antes do ciclo e dentro dele
Bandeira (flag) memória de um acontecimento achou ← Falso

Para o máximo e o mínimo, inicializar a 0 é uma armadilha: se todas as notas lidas fossem negativas (num contexto em que isso fizesse sentido) o máximo ficaria em 0, que nem sequer foi lido. A regra segura é partir do primeiro valor lido.

Exemplo resolvido · estatísticas de uma turma

Retomamos o problema do capítulo 2: ler as notas de 5 alunos e mostrar a média, a nota mais alta, a mais baixa e quantos estão aprovados.

INICIO
   total ← 0
   aprovados ← 0
   ESCREVER "Nota do aluno 1 (0 a 20):"
   LER nota
   maior ← nota
   menor ← nota
   total ← nota
   SE nota >= 10 ENTÃO
      aprovados ← aprovados + 1
   FIM SE
   PARA i DE 2 ATÉ 5 FAÇA
      ESCREVER "Nota do aluno ", i, " (0 a 20):"
      LER nota
      total ← total + nota
      SE nota > maior ENTÃO
         maior ← nota
      FIM SE
      SE nota < menor ENTÃO
         menor ← nota
      FIM SE
      SE nota >= 10 ENTÃO
         aprovados ← aprovados + 1
      FIM SE
   FIM PARA
   media ← total / 5
   ESCREVER "Média: ", media
   ESCREVER "Maior: ", maior, "  Menor: ", menor
   ESCREVER "Aprovados: ", aprovados
FIM

Trace com as notas 12, 8, 15, 10 e 18:

Passo nota total maior menor aprovados
Primeira leitura 12 12 12 12 1
i = 2 8 20 12 8 1
i = 3 15 35 15 8 2
i = 4 10 45 15 8 3
i = 5 18 63 18 8 4

No fim: média 63 / 5 = 12.6, maior 18, menor 8, aprovados 4. Estes valores coincidem com o gabarito calculado à mão no planeamento. Quando o algoritmo e o gabarito concordam, ganhamos confiança; quando discordam, descobrimos um erro.

A primeira leitura fora do ciclo pode parecer repetição de código, mas serve para inicializar maior e menor com um valor real. Há soluções alternativas (usar um valor inicial convencional), mas esta é a mais segura.

Exemplo resolvido · estatísticas com um vetor

Se as notas ficarem guardadas num vetor, podem consultar-se mais do que uma vez depois de lidas. Com as mesmas notas 12, 8, 15, 10 e 18:

INICIO
   notas: vetor de 5 reais
   total ← 0
   PARA i DE 1 ATÉ 5 FAÇA
      ESCREVER "Nota do aluno ", i, " (0 a 20):"
      LER notas[i]
      total ← total + notas[i]
   FIM PARA
   maior ← notas[1]
   PARA i DE 2 ATÉ 5 FAÇA
      SE notas[i] > maior ENTÃO
         maior ← notas[i]
      FIM SE
   FIM PARA
   ESCREVER "Média: ", total / 5
   ESCREVER "Maior: ", maior
FIM

O total é 63 e a média 12.6. Para o máximo, maior parte de notas[1] (12), passa a 15 em notas[3] e a 18 em notas[5], e no fim vale 18. Repara que há dois ciclos: só é possível percorrer os dados uma segunda vez porque o vetor os guardou. Sem vetor, o máximo e a média têm de ser calculados num único ciclo, como no exemplo anterior.

Exemplo resolvido · leitura com sentinela e média

Problema: ler idades até ser introduzido -1, que não conta como idade, e mostrar a média. Se não houver idades, avisar.

INICIO
   total ← 0
   contador ← 0
   LER idade
   ENQUANTO idade <> -1 FAÇA
      total ← total + idade
      contador ← contador + 1
      LER idade
   FIM ENQUANTO
   SE contador = 0 ENTÃO
      ESCREVER "Nenhuma idade introduzida."
   SENÃO
      ESCREVER "Média: ", total / contador
   FIM SE
FIM

Trace com a sequência 20, 30, 40, -1:

Iteração idade processada total contador próxima leitura
Antes do ciclo (nenhuma) 0 0 20
1 20 20 1 30
2 30 50 2 40
3 40 90 3 -1
Teste final (nenhuma) 90 3 (nenhuma)

A média é 90 / 3 = 30. A verificação contador = 0 evita uma divisão por zero quando o utilizador escreve logo -1. Repara no padrão do sentinela: uma leitura antes do ciclo, outra no fim do corpo. Se a leitura estivesse no início do corpo, o -1 seria processado como se fosse uma idade.

Funções predefinidas em ação

Exemplo resolvido · distância entre dois pontos

A distância entre os pontos (x1, y1) e (x2, y2) é a raiz quadrada de (x2 - x1)^2 + (y2 - y1)^2.

INICIO
   LER x1
   LER y1
   LER x2
   LER y2
   dx ← x2 - x1
   dy ← y2 - y1
   distancia ← RAIZ(dx * dx + dy * dy)
   ESCREVER "Distância: ", distancia
FIM

Para os pontos (0, 0) e (3, 4): dx = 3, dy = 4, soma dos quadrados 9 + 16 = 25, e RAIZ(25) = 5. Escrevemos dx * dx em vez de usar um operador de potência para evitar dependermos de uma notação específica; ambas são válidas.

Exemplo resolvido · arredondar a duas casas decimais

Uma função ARREDONDAR(x) devolve o inteiro mais próximo. Para obter duas casas decimais, multiplica-se por 100, arredonda-se e divide-se por 100:

valor ← 3.14159
arredondado ← ARREDONDAR(valor * 100) / 100

3.14159 * 100 = 314.159, que arredondado dá 314; dividido por 100, obtém-se 3.14. É um exemplo de como se combinam funções simples para obter funções mais úteis. Outro exemplo é a diferença absoluta entre duas leituras: ABS(a - b) dá sempre um valor não negativo, qualquer que seja a ordem dos operandos.

Precedência das operações

Numa expressão, a ordem de cálculo é: parênteses, depois ^, depois * / DIV MOD, depois + -, da esquerda para a direita em caso de empate. Por isso 2 + 3 * 4 vale 14 e (2 + 3) * 4 vale 20. Na dúvida, usa parênteses: custam pouco e evitam muitos erros.

12. Validação, teste e depuração de algoritmos

Validação de dados

Validar é confirmar que um valor lido está dentro do intervalo ou formato esperado, antes de o usar nos cálculos. Sem validação, dados absurdos (uma nota de 250, uma idade negativa) contaminam todo o resultado. O padrão combina sempre um REPETIR ATÉ com uma condição de validade:

REPETIR
   ESCREVER "Idade (0-120):"
   LER idade
   SE idade < 0 OU idade > 120 ENTÃO
      ESCREVER "Valor fora do intervalo. Tente outra vez."
   FIM SE
ATÉ idade >= 0 E idade <= 120

Cada validação é uma fronteira de confiança: depois dela, o resto do algoritmo pode assumir que o valor está correto. É uma forma de programação defensiva: escrever o algoritmo de modo a que dados inesperados não o façam falhar.

Testar com trace tables

Testar um algoritmo é executá-lo, à mão ou no computador, com dados concretos, e confirmar que o resultado está certo. A ferramenta principal é a trace table, como a do capítulo 8. É essencial escolher casos de teste variados: valores típicos, valores-limite (0, negativos) e valores inválidos.

Depurar: erros comuns e correção

Erro típico Causa Correção
Ciclo infinito variável de controlo não muda garantir incremento/decremento no corpo
Resultado sempre 0 acumulador-produto (multiplicação) inicializado a 0 em vez de 1 inicializar a 1 antes do ciclo
Totalizador nunca aumenta (fica sempre 0) falta a instrução de acumulação (totalizador ← totalizador + valor) dentro do ciclo garantir que a acumulação está dentro do corpo do ciclo
Off-by-one (falta ou sobra uma iteração) limites do PARA mal definidos rever DE/ATÉ

Depurar (debug) é encontrar e corrigir os erros detetados no teste, sempre a partir da causa, não por tentativa. Depois de correto, um algoritmo pode ainda ser otimizado: combinar cálculos numa só passagem pelos dados, eliminar código repetido e escolher a estrutura mais direta, sem mudar o resultado. Todo este trabalho é apoiado por IDEs, compiladores e aplicações de desenho de algoritmos e fluxogramas.

Tipos de validação

A validação não se resume a intervalos. Há vários tipos, e cada um tem a sua condição.

Tipo Pergunta Exemplo de condição
Intervalo O valor está entre dois limites? nota >= 0 E nota <= 20
Obrigatoriedade Foi preenchido? COMPRIMENTO(nome) > 0
Comprimento Tem o tamanho certo? COMPRIMENTO(codigo) = 4
Lista de valores É uma das opções permitidas? opcao = 1 OU opcao = 2 OU opcao = 3
Coerência Combina com outros dados? dataFim >= dataInicio

Exemplo resolvido · validar com mensagem de erro e limite de tentativas

Em situações reais, não se deve prender o utilizador para sempre. Este algoritmo dá no máximo 3 tentativas para introduzir uma nota válida.

INICIO
   tentativas ← 0
   valida ← Falso
   ENQUANTO tentativas < 3 E NÃO valida FAÇA
      ESCREVER "Nota (0 a 20):"
      LER nota
      tentativas ← tentativas + 1
      SE nota >= 0 E nota <= 20 ENTÃO
         valida ← Verdadeiro
      SENÃO
         ESCREVER "Valor inválido."
      FIM SE
   FIM ENQUANTO
   SE valida ENTÃO
      ESCREVER "Nota registada: ", nota
   SENÃO
      ESCREVER "Número de tentativas esgotado."
   FIM SE
FIM

Trace com as leituras 25, -3 e 14:

Tentativa nota valida Mensagem
1 25 Falso Valor inválido.
2 -3 Falso Valor inválido.
3 14 Verdadeiro (nenhuma)

Com as leituras 25, -3 e 40, as três tentativas gastam-se sem valida mudar e o algoritmo escreve «Número de tentativas esgotado.». Há aqui uma combinação de bandeira, contador e validação, as três técnicas a trabalharem juntas.

Desenhar casos de teste

Um bom conjunto de testes cobre três famílias:

Família Objetivo Exemplo para uma nota (0 a 20)
Típicos confirmar o funcionamento normal 7, 12, 16
Limite verificar as fronteiras 0, 9, 10, 19, 20
Inválidos confirmar que são recusados -1, 21, 250

Cada caso tem de ter o resultado esperado calculado antes de executar o algoritmo. Se calculas o esperado depois de ver o resultado, estás a confirmar o que já lá estava, não a testar.

Com a classificação do capítulo 7 (Insuficiente, Suficiente, Bom, Excelente), uma tabela de testes completa seria:

Nota Esperado
0 Insuficiente
9 Insuficiente
10 Suficiente
13 Suficiente
14 Bom
17 Bom
18 Excelente
20 Excelente

Oito testes cobrem todas as fronteiras, e se todos passam, há uma forte garantia de que a estrutura SE está bem escrita.

Exemplo resolvido · depurar uma média

Um aluno escreveu:

LER a
LER b
LER c
media ← a + b + c / 3
ESCREVER media

Para a = 10, b = 12, c = 14, o resultado esperado é (10 + 12 + 14) / 3 = 12. O algoritmo escreve cerca de 26.67. Para encontrar o erro, seguimos a precedência: a divisão faz-se primeiro, 14 / 3 = 4.67, e só depois as somas, 10 + 12 + 4.67 = 26.67. A causa é a ausência de parênteses. Corrigido:

media ← (a + b + c) / 3

O método foi: (1) calcular o esperado, (2) comparar com o obtido, (3) seguir a execução para localizar onde divergem, (4) corrigir a causa. Corrigir sem localizar a causa leva a remendos que escondem o problema.

Técnicas de depuração

Técnica Como se faz
Trace table seguir as variáveis passo a passo, à mão
Escritas intermédias acrescentar ESCREVER temporários para ver os valores a meio do algoritmo
Isolar o problema testar cada subproblema sozinho, como decidimos no planeamento
Explicar em voz alta contar o algoritmo, linha a linha, a um colega: muitas vezes o erro aparece ao explicar
Reduzir o caso repetir o erro com os dados mais pequenos possíveis

Estas técnicas mostram a vantagem de ter decomposto o problema em partes. Um erro num algoritmo de 5 linhas é fácil de achar; num de 80 linhas, só se se puder testar por partes.

Otimizar, com cuidado

Otimizar vem depois de o algoritmo estar correto e testado. Algumas otimizações simples:

Antes Depois Ganho
Dois ciclos, um para a soma e outro para o máximo Um só ciclo a fazer as duas coisas metade das passagens pelos dados
Testar todos os divisores de 2 a n - 1 sem parar Parar assim que se encontra um divisor (como já faz a bandeira primo do capítulo 8) ou testar só até RAIZ(n) menos iterações
Mesma expressão calculada várias vezes guardá-la numa variável menos cálculo e mais legibilidade
Três SE seguidos mutuamente exclusivos um SE encadeado menos testes

Depois de otimizar, volta a correr todos os testes. Uma otimização que muda o resultado não é uma otimização, é um erro novo.

13. Problemas integrados

Este capítulo junta as técnicas anteriores em problemas completos. Segue sempre o ciclo: definir, planear, estruturar, testar.

Exemplo resolvido · caixa de loja com total e desconto

Definição. Ler o preço de cada artigo, até o operador escrever 0. Preços negativos são recusados. No fim, mostrar o número de artigos e o total. Se o total passar de 50 €, aplicar um desconto de 5%. Valores e percentagem são fictícios, para o exercício.

Elemento Definição
Entrada preços (reais), 0 para terminar
Saída número de artigos, total, desconto, total a pagar
Restrições preço não negativo; desconto só acima de 50 €
Constantes LIMITE ← 50, TAXA ← 0.05

Planeamento. (1) inicializar contador e totalizador; (2) ler o primeiro preço; (3) enquanto não for 0, validar, somar, contar e ler o seguinte; (4) calcular o desconto; (5) mostrar o resumo.

INICIO
   LIMITE ← 50
   TAXA ← 0.05
   total ← 0
   artigos ← 0
   LER preco
   ENQUANTO preco <> 0 FAÇA
      SE preco < 0 ENTÃO
         ESCREVER "Preço inválido, ignorado."
      SENÃO
         total ← total + preco
         artigos ← artigos + 1
      FIM SE
      LER preco
   FIM ENQUANTO
   desconto ← 0
   SE total > LIMITE ENTÃO
      desconto ← total * TAXA
   FIM SE
   ESCREVER "Artigos: ", artigos
   ESCREVER "Total: ", total
   ESCREVER "Desconto: ", desconto
   ESCREVER "A pagar: ", total - desconto
FIM

Teste. Sequência 20, -5, 30, 10, 0.

Leitura processada total artigos Nota
20 20 1
-5 20 1 recusado
30 50 2
10 60 3
0 60 3 termina

Como 60 > 50, desconto = 60 * 0.05 = 3.00, e o valor a pagar é 60 - 3 = 57. Teste-limite: se o total fosse exatamente 50, o desconto não se aplicaria, porque a condição é > e não >=. Este pormenor deve estar escrito no enunciado, e o teste deve confirmá-lo.

Exemplo resolvido · adivinhar o número secreto

Definição. O número secreto é uma constante (7). O jogador tenta adivinhar; o programa diz se o palpite é maior ou menor e conta as tentativas.

INICIO
   SECRETO ← 7
   tentativas ← 0
   REPETIR
      ESCREVER "Palpite:"
      LER palpite
      tentativas ← tentativas + 1
      SE palpite > SECRETO ENTÃO
         ESCREVER "Demasiado alto."
      SENÃO SE palpite < SECRETO ENTÃO
         ESCREVER "Demasiado baixo."
      FIM SE
   ATÉ palpite = SECRETO
   ESCREVER "Acertou em ", tentativas, " tentativas."
FIM

Trace com os palpites 5, 9, 7:

Palpite tentativas Mensagem Sai?
5 1 Demasiado baixo. não
9 2 Demasiado alto. não
7 3 (nenhuma) sim

Escreve «Acertou em 3 tentativas.». Escolhemos REPETIR ATÉ porque o jogador tem de tentar pelo menos uma vez. Num jogo real, o número seria sorteado, o que levaria a uma função específica da linguagem que não faz parte destas funções predefinidas básicas; aqui o objetivo é treinar a estrutura.

Escolher a estrutura certa

Se o problema pede... Usa...
Executar ações por ordem, sem decisões estrutura sequencial
Escolher entre dois caminhos SE ... SENÃO
Escolher entre vários escalões SE encadeado, do mais exigente para o menos
Repetir um número conhecido de vezes PARA
Repetir até um valor especial, sem saber quantas vezes ENQUANTO com sentinela
Pedir e repetir até o dado ser aceitável REPETIR ATÉ
Contar casos contador
Somar valores totalizador
Memorizar se algo aconteceu bandeira lógica

Como se relacionam as realizações da UC

As cinco realizações do referencial correspondem a momentos do mesmo trabalho: definir o problema (capítulos 1 e 2), planear as etapas (capítulos 2 e 3), estruturar em pseudocódigo (capítulos 4 a 9, 11 e 12), desenhar em fluxograma (capítulo 10) e testar e depurar (capítulo 12). Os problemas integrados deste capítulo mobilizam todas as cinco, e são o modelo para o projeto e para a ficha de avaliação.

Erros comuns

Glossário

Síntese

Construir um algoritmo segue sempre a mesma ordem: definir o problema (inputs, outputs, restrições) → planear as etapas (decompor e ordenar) → estruturar em pseudocódigo, com constantes e variáveis, as estruturas sequencial, alternativa e repetitiva (PARA, ENQUANTO, REPETIR ATÉ), vetores, contadores, totalizadores, expressões e funções predefinidas, e validação de dados → desenhar em fluxograma (símbolos ISO 5807) → testar e depurar, com trace tables, casos típicos, limite e inválidos, e por fim otimizar, sempre voltando a correr os testes. Os problemas integrados do capítulo 13 mostram as cinco realizações a trabalhar em conjunto. Domina este ciclo e estarás pronto para aprender qualquer linguagem de programação.

Exercícios resolvidos

1. Classifica cada ciclo pelo tipo mais adequado: (a) imprimir os múltiplos de 3 até 30; (b) ler senhas até o utilizador acertar; (c) ler números até aparecer um 0, sem saber quantos virão.

Resolução: (a) PARA, o número de iterações é conhecido (10 múltiplos); (b) REPETIR ATÉ, executa pelo menos uma vez e só sai quando acerta; (c) ENQUANTO, o número de leituras não é conhecido à partida.

2. Um algoritmo calcula soma ← 0; PARA i DE 1 ATÉ n FAÇA soma ← soma * i FIM PARA. Qual é o erro?

Resolução: usa * (multiplicação) em vez de + (adição) para calcular uma soma, e a inicialização soma ← 0 torna o produto sempre 0. O erro combina lógica errada (operador) com inicialização incompatível com um acumulador-produto.

3. Porque é que ENQUANTO conta <> 10 FAÇA conta ← conta + 3 FIM ENQUANTO, partindo de conta ← 0, nunca termina?

Resolução: conta toma os valores 0, 3, 6, 9, 12, 15, … e nunca é exatamente 10, porque o incremento de 3 "salta" por cima desse valor. A condição conta <> 10 mantém-se sempre verdadeira. Ciclo infinito; a correção seria usar conta < 10 como condição.

4. Distingue um contador de um totalizador e dá um exemplo de cada um no mesmo algoritmo.

Resolução: o contador soma sempre a mesma quantidade fixa (normalmente 1), por exemplo contador ← contador + 1 para contar quantas notas foram lidas. O totalizador soma valores variáveis, por exemplo soma ← soma + nota. Usados em conjunto, permitem calcular uma média: media ← soma / contador.

5. Um algoritmo lê a idade do utilizador sem a validar. Porque é isso um problema, e como corrigir?

Resolução: sem validação, um valor absurdo (por exemplo, uma idade negativa ou 300) é aceite e contamina os cálculos seguintes. A correção passa por um REPETIR ATÉ que só aceita valores dentro de um intervalo razoável (por exemplo, 0 a 120), mostrando uma mensagem de erro em cada tentativa inválida.

6. Escreve um algoritmo que leia um número inteiro e diga se é par ou ímpar. Testa-o com 10, 7 e 0.

Resolução: SE n MOD 2 = 0 ENTÃO ESCREVER "par" SENÃO ESCREVER "ímpar" FIM SE. Com 10, 10 MOD 2 = 0, par. Com 7, 7 MOD 2 = 1, ímpar. Com 0, 0 MOD 2 = 0, par; o zero é um valor-limite e o algoritmo trata-o bem.

7. Converte 4000 segundos em horas, minutos e segundos.

Resolução: 4000 DIV 3600 = 1 hora, resto 4000 MOD 3600 = 400; 400 DIV 60 = 6 minutos, resto 400 MOD 60 = 40 segundos. Resultado: 1 h 6 min 40 s. Contraprova: 3600 + 360 + 40 = 4000.

8. O ciclo n ← 5072; soma ← 0; ENQUANTO n > 0 FAÇA soma ← soma + n MOD 10; n ← n DIV 10 FIM ENQUANTO termina? Qual é o valor final de soma?

Resolução: termina, porque n diminui a cada iteração e chega a 0. Os valores de n são 5072, 507, 50, 5 e 0; os algarismos somados são 2, 7, 0 e 5. A soma final é 14.

9. Um algoritmo classifica notas com SE nota >= 10 ... SENÃO SE nota >= 14 ... SENÃO SE nota >= 18 .... Que classificação recebe o 16 e porquê está errado?

Resolução: recebe a do primeiro ramo (a de >= 10, «Suficiente»), porque 16 >= 10 é verdadeiro e os outros testes nunca são feitos; devia receber a do segundo ramo (>= 14, «Bom»). Deve-se testar do mais exigente para o menos exigente: primeiro >= 18, depois >= 14, depois >= 10.

10. Constrói a trace table de um algoritmo que lê as notas 9, 14, 10, 7 e 20 e calcula total, maior e aprovados (nota igual ou superior a 10).

Resolução: após cada leitura, total toma os valores 9, 23, 33, 40 e 60; maior toma 9, 14, 14, 14 e 20; aprovados toma 0, 1, 2, 2 e 3. Média final: 60 / 5 = 12.

11. Porque é que, ao calcular a média de idades lidas até ao sentinela -1, convém testar contador = 0 antes da divisão?

Resolução: se o utilizador escrever -1 logo à primeira leitura, contador fica a 0 e a divisão total / contador seria uma divisão por zero. O teste permite mostrar uma mensagem em vez de falhar. Com as leituras 15, 25 e -1, contador é 2, total é 40 e a média é 40 / 2 = 20.

12. Corrige media ← a + b + c / 3 e mostra que a versão corrigida dá 13 para 9, 12 e 18.

Resolução: media ← (a + b + c) / 3. Com os valores dados, (9 + 12 + 18) / 3 = 39 / 3 = 13. A versão errada dava 9 + 12 + 18 / 3 = 9 + 12 + 6 = 27, porque a divisão tem precedência sobre a soma.

13. Num vetor temp de 4 reais, guardaram-se as temperaturas 18, 21, 19 e 24. Qual é o valor de temp[3], qual é a média, e o que acontece com temp[5]?

Resolução: temp[3] vale 19, porque o índice começa em 1. A soma é 18 + 21 + 19 + 24 = 82 e a média é 82 / 4 = 20.5. O índice 5 está fora do vetor, que só tem posições de 1 a 4: aceder-lhe é um erro.