Instituto Federal de Mato Grosso do SulNovembro/2021

06. Loops
Algoritmos 2
Prof. Rodrigo Duran
Nesta aula vamos:
-
O que são e como utilizar loops (repetição explícita)
Vamos aprender a sintaxe e o comportamento das estruturas de repetição implícita em JavaScript: for, while, for off. Vocês também vão aprender a compreender códigos que utilizam os diversos tipos de loop e aprender os planos mais comuns em que usamos a repetição explícita.
LOOPS
Looping basicamente refere-se ao ato de voltar sempre ao mesmo lugar.
Algumas vezes fazemos coisas diferentes dentro desse "loop", outras vezes não.
Na maioria das vezes o loop deve acabar em algum momento.
Repetição Explícita
As higher-order functions (HoF) Map, Filter e Reduce proviam um método implícito de repetição dentro de arrays.
As HoF cuidavam dos limites da repetição :quem é o primeiro elemento? Quem é o último? como avançamos nos elementos?
Agora vamos estudar métodos explícitos que podem ser aplicados a qualquer tipo de estrutura e situação. Mas cabe ao programador efetuar os controles...
Repetição: Principios básicos
De forma geral, a repetição consiste em executar a mesma tarefa, ou uma tarefa muito similar, um determinado número de vezes.
Nosso objetivo é identificar a tarefa núcleo dessa repetição: o que é sempre igual?
Por exemplo, imagine que você queira imprimir cada elemento do array time:
let time = ["Taffarel", "Aldair", "Marcio Santos", "Romario"]
Uma forma seria imprimir cada um dos elementos, um a um:
console.log(time[0])
console.log(time[1])
console.log(time[2])
console.log(time[3])
Enquanto essa solução é funcional, ela não é escalável nem tão pouco generalizável. Nós temos que saber de antemão quantos elementos existem no array. Tente utilizar a mesma solução para um array de 1000 elementos!
Repetição: Principios básicos
No exemplo anterior, a tarefa núcleo é imprimir os elementos do array. Veja que ela se repete de forma similar, mas não igual. Entre um passo e outro, o índice do array aumenta para que possamos acessar os próximos elementos do array até o seu final.
De forma geral, toda repetição (implicita ou explicita) segue um conjunto de princípios:
- Um início: Em geral utilizamos um elemento de controle para nos dizer aonde essa repetição começa. Onde eu começo?
- Um fim: Em geral, a repetição deve terminar em algum momento. Devemos fornecer um teste que diga se a repetição deve terminar ou não. Onde eu termino?
- Um incremento: O incremento atua em algum elemento de controle. O incremento é a ação que nos leva do ponto de início até chegarmos ao ponto final.
Repetições Implícitas usando ... Map!
Já observamos que o map é um tipo de repetição implícita pois usa os três elementos descritos anteriormente:
let imprime = function(valor, index){
console.log(valor)
}
let time = ["Taffarel", "Aldair", "Marcio Santos", "Romario"]
time.map(imprime)

Repetições Explícitas usando ... for (também chamado de for padrão)
Outra forma de repetição é utilizar uma estrutura de loop (laço) explícita onde todos os princípios da repetição são representados em sua sintaxe.
O código abaixo obtém o mesmo resultado do exemplo anterior (utilizando o map)
let time = ["Taffarel", "Aldair", "Marcio Santos", "Romario"]
for (let i = 0; i < time.length; i++){
console.log(time[i])
}

Template 1: Contador
Uma das formas mais comuns de se usar loops é repetir uma determinada atividade um número vezes. Esse número de repetições pode ser determinado por um valor inteiro (e.g., 10) ou ser um valor armazenado em uma variável.
for (let i = 0; i < 3; i++){
console.log(i)
}

QUIZ!
Entre no link - AQUI
Se quiséssemos fazer um loop contador de 1 a 10 o correto seria:
Opção 1
for (let i = 1; i < 10; i++){
Opção 2
for (let i = 1; i <= 10; i++){
Opção 3
for (let i = 0; i < 10; i++){
Opção 4
for (let i = 0; i <= 10; i++){
Template 2: "Varrer" (ou percorrer) todos os elementos do array
Outro template muito utilizado é usar um loop para acessar todos os índice (e elementos de um array) para, por exemplo, somar o valor de todos os seus elementos (muito similar ao reduce).
let numeros = [1, 2 , 4, 5]
let soma = 0
for (let i = 0; i < numeros.length; i++){
soma = soma + numeros[i]
}
console.log(soma)
Visualizando o Template 2
Template 2 (Versão alternativa): Percorrer todos os elementos do array usando o for of
Uma outra forma de percorrer todos os elementos de um array é utilizar o for of
. Diferente do for tradicional, não existe uma variável de controle e vários dos principios da repetição estão implícitos no código (assim como nas HoF).
let numeros = [1, 2 , 4, 5]
let soma = 0
for (n of numeros){
soma = soma + n
}
console.log(soma)

QUIZ!
Se quiséssemos imprimir todos os elementos de um array o correto seria:
Opção 1
let valores = [1, 2 , 3, 4 , 5]
for (valor of valores){
console.log(valor)
}
Opção 2
let valores = [1, 2 , 3, 4 , 5]
for (valor in valores){
console.log(valor)
}
Opção 3
let valores = [1, 2 , 3, 4 , 5]
for (valores){
console.log(valores[i])
}
Opção 4
let valores = [1, 2 , 3, 4 , 5]
for (valores){
console.log(valores[valor])
}
Template 1 (Versão alternativa): while
loops!
Existe uma outra forma de criar loops de repetição em JavaScript utilizando o while
Assim como todas as estruturas de repetição, todos os três principios estão presentes. Assim como no for
, os princípios são explícitos utilizando uma variável de controle. Entretanto, diferente do forfor
, a estrutura do while
não "obriga" o usuário a declarar os três princípios na sintaxe do while
let i = 0
while (i<3){
console.log(i)
i++
}

Template 3: Faça algo até achar o sentinela com while
loops!
while
loops são muito úteis quando não existe um número explicito de repetições. Por exemplo, gostaríamos de somar os valores de um array até achar um elemento especial, que chamaremos de Sentinela. Essa sentinela pode ser uma frase do usuário ("Parar!"), um valor negativo, ou um valor especial que denotamos como fim de entrada (99999).
Nesse caso, o while
não vai mais se utilizar de um contador, mas sim verificar se o valor atual corresponde à sentinela. Contruímos uma expressão booleana que só é verdade enquanto o valor atual não é igual à sentinela.
let valores = [0, 22, 33, 11, 20, 44, 99999, 5, 12, 44]
let atual = valores[0]
let soma = 0
let i = 0
while (atual != 99999){
soma = soma + atual
i++
atual = valores[i]
}
console.log(soma)

QUIZ!
Qual o valor impresso no console após executar o programa abaixo?
let letras = ["a", "l", "g", "o", "2", ".", "i", "f"]
let frase = ""
let i = 0
let letra = letras[i]
while (letra != "."){
console.log(letra)
frase = frase + letra
i++
letra = letras[i]
}
frase = frase + letras[i]
console.log(frase)
Template 3: loops dentro de loops?
Todas as estruturas que possuem blocos (if, while, for
) podem ser aninhadas (nested).
No caso particular de loops, quando tempos um loop dentro do outro, isso significa que:
- O primeiro loop é "ativado" - a variável de controle do loop mais externo é inicializada.
- O segundo loop é "ativado" - a variável de controle do loop mais interno -- que deve ser obrigatoriamente diferente da variável do loop mais externo -- é inicializada.
- O loop mais interno faz todos os seus passos até o seu fim.
- O controle retorna ao loop mais externo - a variável de controle é incrementada.
- O loop mais interno reinicia -- sua variável de controle é re-inicializada.
- Esse processo se repete até que o loop externo tenha feito todos os seus passos.
for (let i=0; i<3; i++){
for (let j=0; j<2; j++){
console.log(i, j)
}
}

São tantos tipos de repetição!
Quando e como devo usar cada um deles?
Existe uma regra?
Não!
Todos os tipos de repetição apresentados anteriormente solucionam os mesmos problemas: HoF, for, while, etc.
Existem certos benefícios e malefícios de cada uma das abordagens. Mas esses benefícios são largamente influenciados pela sua familiaridade com cada um dos tipos de repetição. Vejamos:
- Higher-order FunctionsHoF provém uma abordagem funcional, limpa e poderosa para repetição. As HoF livram o usuário de se preocupar com vários detalhes da repetição. Entretanto, essa abordagem implícita pode se tornar um problema, especialmente para iniciantes, pois o modelo de execução deve estar bem claro na mente do programador, além de ser necessário o conhecimento de como criar funções customizadas. Algumas tarefas são mais difíceis de implementar usando HoF, tal como um contador. As HoF em JavaScript estão restritas a repetições em elementos de um array.
- for tradicionalTalvez seja a forma mais comum de loops. É flexível, mas requer que o usuário conheça os parâmetros da repetição. São muito comuns erros onde o usuário calculou erroneamente o fim da repetição (teste mal elaborado) ou inicializou incorretamente a variável de controle (arrays iniciando no índice 1). Oferece acesso indireto a elementos do array através de índices. Implementam facilmente repetições com um número pré-determinado de passos (e.g. repetir 10 vezes).
- for ofÉ um meio termo entre HoF e o for tradicional. Tem a sintaxe similar ao for, mas usa implicitamente vários elementos da repetição, assim como as HoF. Tem como vantagem a simplicidade. Não oferece acesso ao índice dos elementos do array. Também não cria um novo array como resultado da repetição.
- whileÉ geralmente utilizado em casos onde não é possível determinar o número de repetições antes da execução do loop. Exemplos de uso são casos onde o número de repetições depende de uma ação do usuário (e.g., digitar um determinado valor, clicar em um determinado botão, etc.). Possui como desvantagens a necessidade do usuário conscientemente inserir todos os princípios da repetição do código, no local correto.