Módulo 1 | Aula 1
Introdução à Lógica de Programação
Laços de repetição
Laços, também chamados de loops, são estruturas que permitem a repetição de um determinado trecho de código, facilitando o trabalho de programação ao evitar repetições manuais. Existem diversos tipos de laços nas diferentes linguagens de programação. Alguns autores mencionam até seis formas de laços, mas a maioria das linguagens costuma implementar cerca de três dessas formas, ou até menos.
Os laços podem ser definidos de diferentes formas. Abordaremos dois tipos. O primeiro é o laço chamado de pré-teste, que executa as ações do bloco repetido somente após validar uma condição. Um exemplo é o laço enquanto, ou while em inglês, que segue a seguinte estrutura:
Sintaxe
// Laço ENQUANTO - estrutura de repetição com pré-teste
ENQUANTO (condição) FAÇA {
instruções executadas enquanto a condição é verdadeira
} FIM_ENQUANTO
Exemplo:
Temos uma lista de 100 pacientes e seus pesos e alturas, precisamos calcular o Índice de Massa Corporal (IMC) de cada um deles e verificar se estão dentro da faixa saudável.
Exemplo de aplicação
// Laço ENQUANTO (pré-teste) - calcula IMC de 100 pacientes
N = 100
contador = 1
ENQUANTO (contador <= N) faça {
calcula o IMC e mostra na tela se está ou não dentro da faixa saudável
contador = contador + 1
} fim_enquanto
O código acima seria executado pela primeira vez com a condição: “100 é maior do que 1?” Como a resposta é sim, o IMC é calculado, o resultado é exibido, o contador é incrementado em 1, e a condição é verificada novamente: “100 é maior do que 2?” Novamente sim, e o mesmo procedimento é executado para o segundo paciente. No caso do paciente número 100, a condição seria: “100 é maior ou igual a 100?” A resposta continua sendo sim, então o procedimento é realizado. Na próxima execução, a condição seria “100 é maior do que 101?”, o que retorna falso, encerrando o laço, e o código seguiria para a primeira instrução após o laço.
Outra opção é o laço de pós-teste, que tem como finalidade garantir a execução do bloco de código pelo menos uma vez. Ao final da primeira execução, é verificada uma condição lógica: se for verdadeira, o laço continua; caso contrário, a execução é automaticamente encerrada
O comando para esse caso é o repita / até que observe que a condição fica ao final, sendo preciso o bloco de código ser executado ao menos uma vez.
Sintaxe
// Laço REPITA-ATÉ - estrutura de repetição com pré-teste
REPITA {
bloco de código
} ATÉ_QUE (condição)
Considere que estamos realizando a modelagem de um caso de predição, com a meta de que o erro seja menor do que um valor Y. Durante a modelagem, temos diversos parâmetros que podem ser ajustados e queremos testá-los até que o erro seja inferior a Y. Nesse caso, o laço de pós-teste seria mais adequado, pois precisamos executar a predição pelo menos uma vez. Se, na primeira execução, o erro for menor do que o valor desejado, o processo pode ser encerrado. Caso contrário, continuaremos ajustando os parâmetros e testando a predição até que o erro seja menor do que Y.
Exemplo de aplicação
// Laço REPITA-ATÉ (pós-teste) - ajusta modelo até erro < valor desejado
REPITA
X = criar parâmetros
executar a predição nos dados usando os parâmetros X
Y = modelo(X)
até_que (Y < valor_desejado)
// Executa pelo menos uma vez antes de testar a condição
Estrutura de dados
Até agora exploramos técnicas básicas sobre lógica de programação, como variáveis, tipos de dados, operadores, estruturas de decisão e laços de repetição. Essas técnicas permitem compreender a construção de algoritmos. Utilizamos variáveis simples, que permitem armazenar apenas um valor por vez. No entanto, esse método exigiria a criação de um grande número de variáveis durante o desenvolvimento de um algoritmo. A partir de agora, vamos explorar formas de agrupar dados do mesmo tipo em uma única variável. Falaremos sobre estruturas de dados homogêneas, que também são conhecidas por outros nomes, como vetores, matrizes, arrays, entre outros.
Vetor
Vetores são uma forma simples de armazenar vários valores do mesmo tipo utilizando apenas uma variável. Vamos voltar ao exemplo da gaveta: dentro de uma gaveta temos pequenos compartimentos menores, subdividindo-a em 20 espaços, onde podemos guardar nossos acessórios. Chamaremos essa gaveta de “A”. Dentro de “A”, temos indexadores que representam as posições, como A[1], A[2], A[3], A[4], até A[20]. Em cada um desses espaços podemos armazenar uma informação diferente, mas todas devem ser do mesmo tipo, como apenas números, apenas textos ou apenas valores lógicos. Note que, mesmo assim, criamos apenas uma variável do tipo vetor, que nos permite armazenar 20 valores neste exemplo.
Podemos criar vetores de qualquer tamanho necessário, desde que o computador tenha memória RAM suficiente para isso.
Vetores possuem apenas uma dimensão (uma linha com várias colunas ou uma coluna com várias linhas). No entanto, podemos ter estruturas com mais de uma linha e coluna, chamadas matrizes. No exemplo da imagem da gaveta de talheres, cada tipo de talher (colheres, garfos, facas) está organizado em linha, como se fossem vetores, ou seja, uma sequência de elementos do mesmo tipo.
Matriz
Matrizes são estruturas de dados bidimensionais utilizadas para organizar e manipular informações. Diferente dos vetores, que possuem apenas uma dimensão, as matrizes têm tanto linhas quanto colunas, permitindo o armazenamento de dados em uma grade. Essa estrutura é útil quando precisamos representar tabelas ou qualquer conjunto de informações que requerem organização em duas dimensões. Cada elemento de uma matriz pode ser acessado por sua posição específica, definida pelos índices de linha e coluna, facilitando operações como iteração, busca e modificação de dados.
Supondo que temos a seguinte matriz:
Sintaxe
// Declaração de matriz bidimensional
m = [a, b]
[c, d]
Levando em conta que esse índice começa de 1, podemos acessar a posição de b usando os seguintes index: 1 para a primeira linha e 2 para a segunda coluna.
Sintaxe
// Acessando posições específicas em uma matriz
posição = m[1, 2]
É comum o uso de laços de repetição para preenchimento ou manipulação de vetores e matrizes.
Voltando ao exemplo da gaveta de talheres, se imaginarmos a gaveta vista de cima, com várias fileiras (linhas) e colunas, cada posição ocupada por um tipo de talher em determinada quantidade, temos uma matriz, por meio da qual podemos acessar os dados por linha e coluna.
Função
Funções são blocos de códigos reutilizáveis que podem realizar operações computacionais específicas, como entrada, processamento e saída. A ideia de utilizar funções é que, de maneira geral, problemas complexos exigem algoritmos complexos e o uso de funções pode ajudar a quebrar um problema complexo em vários problemas mais simples (dividir para conquistar). Esse tipo de estrutura usa o conceito de modularidade, que é a divisão de um algoritmo em partes menores, e tem vários benefícios, como:
Quando uma função é definida, ela pode ser usada N vezes em várias partes do algoritmo sem precisar duplicar o código.
O uso de funções pode deixar o código mais fácil de entender e melhorar a legibilidade, já que cada bloco faz uma tarefa específica.
Se você usa uma função várias vezes e precisa alterar, só é necessário alterar em um local, sem a necessidade de modificar várias partes do código.
Em projetos maiores, diferentes programadores podem trabalhar em funções diferentes de forma independente, facilitando o trabalho.
Usando funções podemos testar o funcionamento da função individualmente, permitindo que bugs sejam identificados mais rapidamente.
Uma função tem um início e fim, é identificada por um nome, que é como essa função vai ser utilizada por todo o código. Para usar uma função é preciso escrevê-la antes da sua chamada.
Uma função pode ter parâmetros, que são os dados de entrada, e a função pode ou não retornar um resultado. No exemplo a seguir definimos 2 parâmetros de entrada, e usamos a palavra especial para retornar um valor chamada de retorno, a sintaxe é retorno [variável].
Sintaxe
// Definição e uso de funções
função somar(A, B) {
retorno A + B // retorna a soma de A e B
}
// Chamando a função
resultado = somar(1, 3)
mostrar(resultado) // 4
resultado = somar(100, 200)
mostrar(resultado) // 300
Observe que, no primeiro exemplo anterior, enviamos para a função somar os números 1 e 3, que chamaremos de argumentos, enquanto eles serão atribuídos de forma correspondente aos parâmetros A e B que foram definidos na função somar. Quando estamos no âmbito de uma função, as variáveis passam a ter um contexto local, ou seja, os parâmetros dentro de uma função só existem dentro daquela função e não são visíveis ao código principal, enquanto às variáveis que ficam fora da função, e dentro do código principal, chamamos de variáveis de contexto global. Vamos a um exemplo.
Sintaxe
// Escopo de variáveis - variáveis globais e locais
A = 100 // Variável global
// Função com parâmetro chamado A (diferente do A global)
função multiplicar(A, multiplicador) {
produto = A * multiplicador // Usa o A do parâmetro, não o A global
retorno produto
}
resposta = multiplicar(5, 3) // Passa 5 como parâmetro A e 3 como multiplicador
mostrar(A) // 100 (A global permanece 100)
mostrar(resposta) // 15 (usou A = 5 como parâmetro, então 5 * 3 = 15)
// O A dentro da função (parâmetro) é diferente do A fora (global)
// Mesmo tendo o mesmo nome, são variáveis independentes