Escolar Documentos
Profissional Documentos
Cultura Documentos
Reitor
JOSÉ CARLOS DE SÁ
Conselho Editorial
Presidente
Nesse contexto, imaginem que um/a colega nosso não sabia como conectar seu
celular na rede sem fio da casa dele/a, como poderíamos ajudá-lo/a?
7 Clique em conectar.
Voltemos, agora para o nosso exemplo 1.1, notem como ele contém sete passos e
como executamos os passos do primeiro até o sétimo para obtermos o resultado
esperado.
Apesar de permitir que nosso/a colega possa conectar seu celular à rede, o
exemplo 1.1 é apenas uma das formas de resolvermos o problema. Há diversas
formas de conectar um celular à internet. Algumas são mais rápidas, outras são mais
fáceis, cada solução tem suas particularidades e cabe a nós, programadores, escolher
a melhor solução para um determinado problema em um contexto específico.
Capítulo 1: Algoritmos
Lógica de Programação com node.js
Entradas
Passos
7 Clique em conectar.
Saídas
Capítulo 1: Algoritmos
Lógica de Programação com node.js
Instruções de baixo nível de abstração são aquelas que representam detalhes bem
específicos de uma ação. Por exemplo, o ato de abrir uma porta pode ser detalhado
nas seguintes instruções: 1 - coloque a mão na maçaneta; 2 - gire a maçaneta; 3 -
empurre a porta para finalmente abri-la.
Até agora sabemos que algoritmos são capazes de descrever soluções para
diversos problemas distintos, mas ainda falta compreendermos a relação entre um
algoritmo e um programa de computador.
Capítulo 1: Algoritmos
Lógica de Programação com node.js
Capítulo 1: Algoritmos
Lógica de Programação com node.js
Exemplo 1.2 - Programa que soma dois números (escrito na linguagem JavaScript).
1 numeroA = 10 // entrada
2 numeroB = 15 // entrada
Uma execução condicional ocorre quando temos mais de uma opção a ser seguida
em um algoritmo. Por exemplo, quando vamos lavar uma camisa podemos lavá-la
manualmente ou utilizar uma máquina de lavar, ou ainda quando vamos fritar um ovo
podemos utilizar manteiga ou margarina. Nesses casos o algoritmo precisa deixar
claro qual condição será seguida, algo como:
Notem que nos passos acima, há uma condição bem definida: a existência de uma
máquina de lavar. Quando a máquina está disponível podemos usá-la, mas quando
não está vamos realizar a lavagem manualmente.
Passos (ou códigos) nesse formato se … caso contrário são muito comuns em
linguagens de programação, porém as palavras utilizadas para eles são em inglês: if …
else.
Nesse contexto, a palavra enquanto representa que vamos ter que esfregar a
bucha repetidas vezes até que a condição de haver sujeira na panela torne-se falsa.
Assim como na execução condicional, quando precisamos utilizar laços nos nossos
programas utilizamos termos em inglês. Então, no caso da palavra enquanto, vamos
Capítulo 1: Algoritmos
Lógica de Programação com node.js
utilizar a sua versão em inglês: a palavra while (a palavra for também é muito usada
em laços).
Capítulo 1: Algoritmos
2. OPERADORES, TIPOS E VARIÁVEIS
Os textos são compostos por cadeias de caracteres. Por exemplo, o texto "bola" é
composto pelos caracteres 'b', 'o', 'l' e 'a'.
Também podemos escrever frases na língua portuguesa que são compostas tanto
por números quanto por textos, por exemplo: "A bola custou 5 reais". Nestes casos, as
linguagens tendem a tratar a informação como um texto.
A tabela abaixo apresenta uma visão geral desses dois tipos na linguagem.
Exemplos no
Tipo Dados que representa
Node.js
'a'
"a"
String Cadeias de caracteres
'Abacate'
"abacate"
Tabela 2.1 - Visão geral dos tipos number e string.
É necessário que o conteúdo da string esteja entre aspas, por exemplo, o texto 'a
não representa uma string bem formada, mas sim um erro de sintaxe que impediria a
execução do programa.
Podemos utilizar tanto as aspas simples ' como as aspas duplas " para representar
strings. Por exemplo, tanto 'bola' como "bola" representam igualmente a palavra bola.
5 representa um número, porém "5" e '5' são strings. Representar números como
strings ou vice-versa é um erro muito comum entre iniciantes.
2.2. Operadores
Cada tipo tem um conjunto de operadores que podem ser aplicados a eles, como
veremos a seguir.
Exemplo no node.js
Soma + 4+5 9
Subtração - 10 - 3 7
Multiplicação * 2*6 12
Divisão / 8/2 4
Exponenciação ** 5**2 25
Tabela 2.2 - Principais operadores aritméticos do tipo number.
2 2
12 % 5
(note que 2 vezes 5 é igual a 10) (note que 12 - 10 = 2)
1 3
7%4
(note que 1 vez 4 é igual a 4) (note que 7 - 4 = 3)
4 0
8%2
(note que 4 vezes 2 é igual a 8) (note que 8 - 8 = 0)
Tabela 2.3 - Visão geral do operador %.
Assim como no tipo number, há diversos operadores que podem ser utilizados com
strings. Nesta seção, vamos focar apenas no operador da concatenação representado
pelo símbolo +.
Expressão Resultado
Expressão Resultado
10 + "carros" "10carros"
"carros" + 10 "carros10"
20 + "carros" + 10 "20carros10"
Tabela 2.5 - Exemplos de uso do operador + entre strings e números.
10 + 5 * 2
1
Operator precedence - JavaScript _ MDN: https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence
2.3. Variáveis
Agora que já conhecemos como realizar operações com os dois tipos básicos do
node.js, o próximo passo é aprender como armazenar os resultados dessas operações
na memória do programa, as variáveis são justamente o mecanismo que utilizamos
para tal.
A primeira coisa que podemos notar é que um quadrado é composto por 4 lados
com valores idênticos (quatro lados iguais), caso contrário, ele não seria um quadrado.
Ambas as expressões são corretas, mas vamos optar pela segunda opção porque
ela é mais curta e faz apenas uma menção à variável lado.
1 lado = 10
2 perimetro = 4 * lado
Dado o novo operador, vamos escrever uma nova versão da nossa resposta, desta
vez com comentários (textos que serão ignorados pelo interpretador, quando o
programa for executado) informando o que acontece em cada linha de código.
2 perimetro = 4 * lado 10 40
Por fim, ainda nos resta adicionar uma funcionalidade muito importante ao nosso
programa: a capacidade do programa de nos informar na tela do computador o valor
das computações realizadas.
Isso pode ser feito através da função console.log() que imprime o valor de
variáveis ou expressões na tela do computador.
3 console.log(perimetro) 10 40 40
Mas como isso ocorre na prática? Vamos agora observar o que ocorre quando
salvamos nosso código em um arquivo chamado perimetro.js e o executamos através
do comando:
node perimetro.js
if (condição) {
Código executado quando a condição é verdadeira
} else {
Código executado quando a condição é falsa
}
Além disso, o bloco else é opcional, devendo ser utilizado apenas quando
necessário.
Por fim, é importante observar que as chaves { } também são opcionais, mas
utilizá-las é uma boa prática de programação especialmente em código grandes, onde
cada condição engloba múltiplas linhas.
Nossa solução será construída a partir de uma estrutura simples, mas que será
melhorada no decorrer deste capítulo.
Solução #1:
9 }
11 console.log("fim do programa")
Ou seja, como a expressão numero > 0 tem como resultado true, a linha 8 do
programa é executada.
Solução #2:
9 } else {
11 }
12
13 console.log("fim do programa")
Ou seja, como a expressão numero > 0 tem como resultado true, a linha 8 do
programa é executada, assim como na primeira solução.
Assim, na segunda execução, o que ocorre é que a expressão numero > 0 tem
como resultado false, e por tanto a linha 8 do código é ignorada, e o programa dá um
salto para a linha 10, onde é impressa a mensagem que o número não é positivo.
if (condição1) {
Código executado quando condição1 é verdadeira
} else if (condição2) {
Código executado quando condição1 é falsa e a condição2 é
verdadeira
} else {
Código executado quando condição1 e condição2 são falsas
}
Esse novo bloco nos permite selecionar exatamente quais linhas do programa
serão executadas quando uma condição é verdadeira.
Para compreender melhor como o bloco funciona, vamos mais uma vez
desenvolver uma solução para o problema de como converter os números entre 0 e 9
em strings:
7 console.log("zero")
9 console.log("um")
11 console.log("dois")
13 console.log("tres")
15 console.log("quatro")
17 console.log("cinco")
19 console.log("seis")
21 console.log("sete")
23 console.log("oito")
25 console.log("nove")
26 } else {
27 console.log("numero desconhecido")
28 }
29
30 console.log("fim do programa")
Nossa solução possui diversas condições, uma para cada número. Cada condição
converte um número para sua string correspondente. Vamos ver agora qual a saída
dela com valores distintos para a variável numero (linha 1).
Quando
Quando
Quando
Uma alternativa ao bloco if … else if ... else é o bloco switch … case. Ele nos
permite implementar programas similares, porém que podem ser mais fáceis de serem
compreendidos em determinados contextos.
Dessa forma, um switch … case vai nos permitir testar diversas condições e
executar um código específico para cada uma delas.
switch (variável) {
case valor1:
Código executado quando a variável tem o mesmo valor de
valor1
break // break é opcional, mas garante que o código só
execute o código específico para um dos valores que a variável pode
assumir
case valor2:
Código executado quando a variável tem o mesmo valor de
valor2
break
case valor3:
Código executado quando a variável tem o mesmo valor de
valor3
break
case valor4:
Código executado quando a variável tem o mesmo valor de
valor4
break
// …
default:
Código executado quando a variável tem nenhum dos valores
listados nos cases
}
Como o bloco switch … case funciona como uma alternativa ao bloco if … else if ...
else, podemos implementar novamente o exemplo 1.3, porém utilizando essa nova
construção:
Exemplo 3.4 - Converte um número para uma string usando um bloco switch … case
6 switch (numero) {
8 console.log("zero")
9 break
11 console.log("um")
12 break
14 console.log("dois")
15 break
17 console.log("três")
18 break
20 console.log("quatro")
21 break
23 console.log("cinco")
24 break
26 console.log("seis")
27 break
29 console.log("sete")
30 break
32 console.log("oito")
33 break
35 console.log("nove")
36 break
37 default:
38 console.log("numero desconhecido")
40
41 console.log("fim do programa")
O primeiro fato que chama a atenção no exemplo 3.4 é o tamanho do código, com
41 linhas ele possui 11 linhas a mais que o exemplo 3.3 (que resolve o mesmo
problema, porém com if’s e else’s). Com tudo, pela forma que o switch … case é
escrito, o exemplo 3.4 muitas vezes pode ser compreendido mais facilmente,
especialmente por principiantes.
Assim como no exemplo anterior, a solução com um bloco switch … case possui
diversas condições, uma para cada número. Cada condição converte um número para
sua string correspondente. Assim, a saída também é idêntica ao exemplo 3.3, com
valores distintos para a variável numero (linha 1).
Quando
Quando
Quando
Outra peça importante do exemplo 3.4 é a palavra reservada break, ela garante que
quando uma condição é atendida, o código do bloco é encerrado, fazendo com que
nenhuma outra condição seja executada. Ou seja, quando o programa chega em um
break, o bloco switch … case tem a sua execução encerrada.
Essa diferença é fundamental para sabermos como escolher entre elas na hora de
escrevermos um programa. Para esclarecer a diferença, precisamos revisar cada uma
de forma independente.
if (condição1) {
Código executado quando condição1 é verdadeira
}
Quadro 3.1 - casos de teste para um programa que testa se um número é maior que
10 ou par.
Entrada (valor do
número)
Caso de teste Saída no console
É maior que 10 É par
maior que 10
1 Sim Sim
par
5 }
8 console.log("par")
9 }
10
11 console.log("fim do programa")
6 } else if (numero % 2 == 0) {
7 console.log("par")
8 }
10 console.log("fim do programa")
Essa solução geraria o seguinte resultado quando executada (note como ela falha
em reportar que o número é par no caso de teste 1):
Código var a = 10
var b = 5
var maximo = a
if (b > a) {
maximo = b
}
if … if
Quando Quando há dois testes a serem feitos, mas eles não são excludentes
usar (ambos podem ocorrer ao mesmo tempo)
Código var a = 10
if (a > 10) {
console.log("maior que 10")
}
if (a % 2 == 0) {
console.log("par")
}
if … else
Quando Quando há dois testes excludentes (se um ocorrer, o outro não ocorre) a
usar serem feitos
Código var a = 10
if (a % 2 == 0) {
console.log("par")
} else {
console.log("ímpar")
}
if … else if
Quando Quando há dois testes a serem feitos, mas eles não são excludentes
usar
Exemplo Testar se um número é positivo ou negativo (note que ele pode ser zero e
por tanto não ser positivo nem negativo)
Código var a = 10
if (a > 0) {
console.log("positivo")
} else if (a < 0) {
console.log("negativo")
}
if … else if … else
Exemplo Indicar se a nota de um estudante significa que ele foi aprovado por
média, vai para a final ou se foi reprovado direto.
if (nota >= 6) {
console.log("aprovado")
} else if (nota < 4) {
console.log("reprovado direto")
} else {
console.log("recuperação")
}
switch … case
A palavra “e” é utilizada para conectar frase e expressões, muitas vezes dando um
significado de que as expressões conectadas devem ser verdadeiras ao mesmo
tempo, por exemplo:
Está chovendo e fazendo sol (afirma que as duas coisas estão ocorrendo
ao mesmo tempo);
O carro é novo e azul (descreve duas características do carro);
A mesa é de madeira e possui quatro cadeiras? (pergunta se duas
características da mesa são verdadeiras).
verdadeiras
Uma das formas mais simples de compreender esse operador é através de uma
tabela com todas as suas possibilidades:
true false
false true
1 var a = true
2 var b = false
3 var c = 10
4 var d = 5
8 } else {
10 }
12
13 console.log("fim do programa")
fim do programa
Porém, quando a variável numero assume o valor -3, a saída do nosso exemplo no
console seria:
1 var a = true
2 var b = false
3 var c = 10
4 var d = 5
Exemplo 4.4 - Testa se um número é par ou maior que dez ao mesmo tempo.
9 } else {
12 }
13
14 console.log("fim do programa")
Notem que o programa passa no teste porque o 16 é par e maior que 10.
Notem que o programa não passa no teste porque o 6 apesar de ser par, não é
maior que 10.
Notem que o programa não passa no teste porque o 13 apesar de ser maior que
10, não é par.
Notem que o programa não passa no teste porque o 5 não é maior que 10 e
também não é par.
Em resumo basta que qualquer dos operandos seja verdadeiro que a disjunção
entre eles também será verdadeira.
1 var a = true
2 var b = false
3 var c = 10
4 var d = 5
9 } else {
12 }
13
14 console.log("fim do programa")
Notem que o programa passa no teste porque o 16 é par e maior que zero.
Notem que o programa passa no teste porque o 6 é par, mesmo não sendo maior
que zero.
Notem que o programa passa no teste porque o 13 é maior que 10, mesmo não
sendo par.
Notem que o programa não passa no teste porque o 5 não é maior que 10, nem é
par.
Exemplo 4.7 - Dados dois números, identificar quando ambos os números são pares
ou maiores que 10.
10 } else {
13 }
14
15 console.log("fim do programa")
3 enquanto há camisas
8 desligue o ferro
9 feche o guarda-roupas
A imagem 5.1 ilustra como o exemplo 5.1 é executado. Na imagem podemos ver
que há um conjunto de passos que é executado enquanto houver camisas a serem
passadas. No momento em que não houver mais camisas, o algoritmo irá prosseguir
com a execução dos seus passos restantes até o seu fim.
Capítulo 5: Laços
Lógica de Programação com node.js
1 enquanto condição
4 ...
5 fim da repetição
Capítulo 5: Laços
Lógica de Programação com node.js
5.1. while
1 while (condição) {
4 ...
5 }
1 var inicio = 5
2 var fim = 10
Capítulo 5: Laços
Lógica de Programação com node.js
infinito
10 }
11
12 console.log("fim do programa")
Nosso exemplo possui como entrada dois números distintos: o início e o fim do
intervalo a ser impresso (linhas 1 e 2). Também utilizamos uma variável auxiliar
chamada numeroAtual que guarda o número a ser impresso no console durante cada
iteração (execução) do laço. Essa variável vai aumentando o seu valor de um em um
cada vez que o laço é executado (linha 9). As linhas 8 e 9 compõem o laço e por isso
são executadas múltiplas vezes. Toda vez que o programa chega na linha 10, ele volta
para a linha 7 e testa novamente a condição do laço. Se a condição for verdadeira, o
laço será executado mais uma vez, se a condição for falsa, o programa sai do laço
pulando para a linha 12.
intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa
intervalo de 10 ate 5
fim do programa
Isso ocorre porque o programa não verifica se seus parâmetros são corretos, para
resolver este problema, vamos recorrer à utilização de um if:
Exemplo 5.5 - Imprime todos os números inteiros entre um intervalo numérico válido.
1 var inicio = 5
2 var fim = 10
Capítulo 5: Laços
Lógica de Programação com node.js
8 console.log(numeroAtual)
9 numeroAtual = numeroAtual + 1
10 }
12 console.log("intervalo invalido")
13 }
14
15 console.log("fim do programa")
intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa
intervalo de 10 ate 5
intervalo invalido
fim do programa
Isso ocorre porque o programa verifica se seus parâmetros são corretos através
de um if.
5.2. for
Capítulo 5: Laços
Lógica de Programação com node.js
O for funciona como uma versão mais compacta do laço while. Nele, a declaração
da variável temporária (comumente um contador) e a sua atualização (comumente um
incremento) são realizadas na mesma linha do teste da condição:
4 ...
5 }
Complicado?
Vamos agora reescrever nosso modelo na ordem que ele é executado pelo
interpretador (notem que como na prática, a execução é praticamente idêntica ao
while):
2 for (; condição;) {
5 ...
Capítulo 5: Laços
Lógica de Programação com node.js
7 }
Por compactar três operações em uma única linha o for pode ser menos intuitivo
para os programadores iniciantes, quando nos acostumarmos com ele iremos utilizá-
lo com muito mais frequência que o while.
1 var inicio = 5
2 var fim = 10
8 }
10 console.log("fim do programa")
Como esperado, a solução para o problema utilizando o for ficou bem menor do
que a solução com o while. Isso é possível porque a linha 6 do código compacta a
declaração da variável auxiliar, a condição do for e o incremento da variável auxiliar.
intervalo de 5 ate 10
5
6
7
8
9
10
Capítulo 5: Laços
Lógica de Programação com node.js
fim do programa
intervalo de 10 ate 5
fim do programa
Exemplo 5.9 - Imprime todos os números inteiros entre um intervalo numérico válido,
utilizando um for.
1 var inicio = 5
2 var fim = 10
9 }
11 console.log("intervalo invalido")
12 }
13
14 console.log("fim do programa")
intervalo de 5 ate 10
5
6
7
8
9
Capítulo 5: Laços
Lógica de Programação com node.js
10
fim do programa
intervalo de 10 ate 5
intervalo invalido
fim do programa
1 do {
4 ...
Capítulo 5: Laços
Lógica de Programação com node.js
código que será executado pelo laço, ela só é testada após a execução do bloco,
consequente, o laço é executado ao menos uma vez antes de parar.
1 var inicio = 5
2 var fim = 10
7 do {
9 numeroAtual = numeroAtual + 1
11
12 console.log("fim do programa")
No exemplo 5.11 é possível observar que o código se inicia de forma idêntica a sua
solução com o while (exemplo 5.4) com as variáveis que definem o fim e o começo do
intervalo a ser impresso nas duas primeiras linhas (1 e 2), em seguida é definida a
variável numeroAtual (linha 3) que será utilizada para guardar cada número que será
impresso dentro do laço. Na linha 7 o programa inicia o laço com a palavra reservada
do e a abertura das chaves. As linhas 8 e 9 contêm a impressão do número no console
e o incremento da variável numeroAtual. Finalmente, a linha 10 contém a condição de
parada do laço que é executada apenas ao final de cada execução do bloco de código
do laço.
intervalo de 5 ate 10
Capítulo 5: Laços
Lógica de Programação com node.js
5
6
7
8
9
10
fim do programa
intervalo de 10 ate 5
10
fim do programa
Assim, é possível observar que mesmo com entradas inválidas, o programa ainda
imprimirá o número 10, isso ocorre porque a condição de parada só é testada ao final
de cada execução do laço fazendo com que o laço seja executado ao menos uma vez.
As soluções anteriores também possuíam problemas com entradas inválidas, porém
nenhum número era impresso nelas.
Mesmo assim, para solucionar o problema com a entradas no exemplo 5.11 vamos
recorrer à mesma solução dos demais exemplos: utilizar um if.
1 var inicio = 5
2 var fim = 10
7 do {
8 console.log(numeroAtual)
9 numeroAtual = numeroAtual + 1
12 console.log("intervalo invalido")
Capítulo 5: Laços
Lógica de Programação com node.js
13 }
14
15 console.log("fim do programa")
intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa
intervalo de 10 ate 5
intervalo invalido
fim do programa
Até o momento, cada um dos laços foi estudado através de programas que
resolvem problemas idênticos. Adotamos essa abordagem para ressaltar que de certa
forma o while, o for e o do … while são equivalentes.
Contudo, cada laço resolve de forma mais adequada (ou elegante) determinados
problemas, um resumo sobre como escolher cada tipo de laço pode encontrado no
quadro 5.1:
Quando Quando há uma condição booleana a ser testada antes da execução com
usar código.
Capítulo 5: Laços
Lógica de Programação com node.js
for
do … while
Quando Quando é preciso executar o laço ao menos uma vez antes de testar a
usar condição de parada
do {
console.log("Digite um 1 para jogar")
console.log("Digite um 2 para sair")
var opcao = lerOpcaoDoUsuario()
} while (opcao != 2)
Além disso, laços também podem ser utilizados em conjunto com eles mesmos.
Assim, não é raro criarmos códigos que possuem laços que contêm outros laços
dentro de si, a esse tipo de estrutura é dado o nome de laço aninhado.
Capítulo 5: Laços
Lógica de Programação com node.js
Para ilustrar como laços aninhados podem ser úteis vamos trabalhar com um
problema onde temos que imprimir no console uma tabela de @ (arrobas) de 10 linhas
por 10 colunas:
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
@@@@@@@@@@
Para implementar um programa que gera a saída acima vamos utilizar laços
aninhados:
1 var linhas = 10
2 var colunas = 10
Capítulo 5: Laços
Lógica de Programação com node.js
12
13 console.log("fim do programa")
O laço que vai da linha 7 até a linha 9 é capaz por imprimir uma única linha de
arrobas:
@@@@@@@@@@
Como já sabemos como imprimir uma linha de arrobas, o que precisamos agora é
garantir que o código que imprime a linha de arrobas seja executado 10 vezes.
Felizmente, sabemos muito bem como executar repetidamente um conjunto de linhas
de código: usando um laço. Assim, o laço que começa na linha 6 e termina na linha 11
garante que o código que imprime uma linha de arrobas seja executado 10 vezes.
console.log("node") node
console.log(".") .
console.log("js") js
process.stdout.write("node") node.js
process.stdout.write(".")
Capítulo 5: Laços
Lógica de Programação com node.js
process.stdout.write("js")
Quando escrevemos laços sempre há o risco de gerarmos um laço que nunca irá
parar fazendo com que nosso programa pareça ter travado ou repetindo sem parar a
mesma ação para a pessoa que o está executando. Quando isso ocorre dizemos que o
programa entrou em um laço infinito ou em um loop infinito.
Exemplo 5.14 - programa que gera um laço infinito usando um while (não o execute).
5 }
Capítulo 5: Laços
Lógica de Programação com node.js
O exemplo 5.15 mostra como podemos alterar o exemplo 5.14 para que não ocorra
um laço infinito:
4 console.log("laco em execucao")
6 }
Quando utilizamos um for, é muito comum que a variável que controla o laço
nunca seja alterada ou seja alterada incorretamente.
Exemplo 5.16 - programa que gera um laço infinito usando um for (não o execute).
4 console.log("laco em execucao")
Capítulo 5: Laços
Lógica de Programação com node.js
5 }
4 console.log("laco em execucao")
5 }
Laços infinitos podem ocorrer toda vez que usamos uma estrutura de repetição.
Para evitá-los, precisamos garantir que nossos laços sejam capazes de gerar as
alterações necessárias nas variáveis que controlam as condições de parada das
estruturas de repetição.
Caso seu programa entre em um laço infinito durante sua execução no console,
podemos utilizar a combinação de teclas ctrl + c para forçar o fim da execução do
programa.
Capítulo 5: Laços
6. SUBPROGRAMAS
No desenvolvimento de grandes projetos de software há três conceitos
fundamentais que têm relação com a qualidade do código (o quão bom o código é).
Esses conceitos são: reusabilidade, legibilidade e manutenibilidade.
De forma geral, um código bem escrito deve buscar ter essas três características.
1 var mes = 5
5 console.log("data valida")
6 } else {
7 console.log("data invalida")
8 }
2
Mais detalhes sobre como testar corretamente se um número é inteiro:
https://2ality.com/2014/05/is-integer.html
Capítulo 6: Subprogramas
Lógica de Programação com node.js
Assim, fica claro que, mesmo funcionando, a solução acima não é a ideal para
projetos grandes, nos levando à necessidade de utilizarmos outras formas de
programá-la.
Para resolver problemas como esses, vamos utilizar um conceito conhecido como
subprograma.
Além disso, podemos dar nomes a um subprograma de forma que ele tenha sua
funcionalidade facilmente compreendida.
4 }
Capítulo 6: Subprogramas
Lógica de Programação com node.js
2 function multiplicar(a, b) {
3 var resultado = a * b
10
11 var x = 5
12 var y = 6
13
Capítulo 6: Subprogramas
Lógica de Programação com node.js
5 5 } Fim da função
11 5 } Fim da função
13 11 var x = 5
14 12 var y = 6
19 5 } Fim da função
Capítulo 6: Subprogramas
Lógica de Programação com node.js
2 function multiplicar(a, b) {
5 }
12
13 var x = 5
Capítulo 6: Subprogramas
Lógica de Programação com node.js
14 var y = 6
2 function validarMes(mes) {
3 return mes % 1 === 0 && mes >= 1 && (mes <= 12)
4 }
Pelo mesmo motivo, ao invés de replicarmos o teste por todo código, agora só
precisamos chamar a função. Assim, se houver um bug no código ele estará isolado
dentro da função e ao corrigirmos a função, temos a correção feita para todos os
testes de validade de mês feitos no código inteiro. Essa abordagem torna o programa
mais manutenível.
Por fim, agora também é fácil reutilizar o código, uma vez que só precisamos
chamar a função.
Capítulo 6: Subprogramas
Lógica de Programação com node.js
Assim, sempre que tivermos que retornar um valor para o trecho de código que
chamou nosso subprograma, deveremos optar por subprogramas funcionais. Os
seguintes exemplos de problemas podem ser naturalmente resolvidos através de
subprogramas funcionais:
Capítulo 6: Subprogramas
Lógica de Programação com node.js
Toda vez que utilizamos as chaves no nosso código { } estamos criando novos
escopos, assim construções como funções, if’s, while’s, for’s e todas as demais
construções que também utilizam chaves possuem escopos próprios.
Quando declaramos uma variável fora das nossas funções ela é tratada como uma
variável global, sendo visível em todo o arquivo porque possui escopo global:
8 }
13
Capítulo 6: Subprogramas
Lógica de Programação com node.js
Por outro lado, quando declaramos variáveis dentro de uma função elas são
consideradas como locais pelo interpretador:
8 }
14
Capítulo 6: Subprogramas
Lógica de Programação com node.js
8 }
13
O exemplo 6.8 ilustra com uma declaração sem o var ou o let acaba gerando uma
variável global independentemente do local onde a variável foi declarada. Porém, a
variável só se torna global quando tem o código da sua declaração executada. É
importante ressaltar que essa é uma característica bem específica do JavaScript e
esse tipo de comportamento não se encontra presente na maioria das linguagens de
programação.
Capítulo 6: Subprogramas
Lógica de Programação com node.js
10
De forma geral, o exemplo 6.9 ilustra como uma variável declarada com o var
torna-se visível assim que seu escopo se inicia (antes mesmo da sua declaração no
código), já uma variável declarada com let ou sem modificador só se torna visível no
momento que a linha com a sua declaração for executada.
Quadro 6.2 - Escopo das variáveis com var, let e sem modificador.
var
Quando Quando precisamos que a variável seja definida antes da sua primeira
usar? utilização. De forma geral não é uma boa ideia usar o var porque o nosso
programa irá continuar sua execução mesmo que a variável não tenha
sido inicializada, o que pode acabar gerando um bug.
let
Capítulo 6: Subprogramas
Lógica de Programação com node.js
Como Variável torna-se localmente visível apenas após a sua declaração, usá-
funciona? la antes da declaração resulta em um erro que faz com que o programa
pare de ser executado.
Sem modificador
Como Variável torna-se globalmente visível apenas após a sua declaração, usá-
funciona? la antes da declaração resulta em um erro que faz com que o programa
pare de ser executado.
Uma situação muito comum em códigos que fazem parte de programas maiores é
a existência de conflitos de escopo. Eles ocorrem quando temos duas variáveis com o
mesmo nome, mas que foram declaradas em contextos distintos:
6 let b = 10
7 // usa o a e o b locais
Capítulo 6: Subprogramas
Lógica de Programação com node.js
9 }
10
12
Capítulo 6: Subprogramas
7. VETORES
Até o momento, os dois principais tipos de dados que estudamos: number e
boolean são atômicos. Ou seja, eles representam conceitos unitários. Por exemplo,
uma variável numérica representa um valor número como 10, 20 ou -14. Já uma
variável booleana representa um valor booleano (true ou false).
Porém, também estudamos, ainda que de forma superficial, o tipo string (texto).
Uma string nada mais é do que uma coleção de caracteres. Os caracteres, por sua vez,
são como os tipos number e boolean, pois cada caractere representa uma letra como
‘A’, ‘B’ ou ‘x’, etc.
Um vetor nada mais é do que uma coleção de dados. Cada elemento dentro dessa
coleção possui um tipo próprio. Assim, dentro de um vetor podemos ter dados dos
tipos number, boolean, strings, ou até mesmo uma coleção de dados (vetor).
Outra característica muito importante dos vetores é que cada elemento dentro dele
possui um índice que funciona como uma espécie de identificador da posição onde o
elemento encontra-se no vetor:
Índice Valor
0 10
1 -5
2 3.14
3 false
4 true
5 ‘bola’
6 -8.75
Tabela 7.1 - Exemplo de um vetor, note como cada elemento possui um índice e um
valor.
Capítulo 7: Vetores
Lógica de Programação com node.js
No decorrer deste capítulo, vamos estudar como criar e manipular vetores com o
node.js.
Como podemos verificar, vetores são variáveis como quaisquer outras. Porém, sua
inicialização é bem diferente: devemos indicar quais são os elementos que compõem
o vetor.
Capítulo 7: Vetores
Lógica de Programação com node.js
Índice Valor
0 3
1 “60”
2 false
Tabela 7.2 - Índices e valores da varíavel vetorHibrido2.
3
Mais sobre construtores no capítulo sobre tipos compostos.
Capítulo 7: Vetores
Lógica de Programação com node.js
Para termos acesso aos valores dos elementos presentes em um vetor fazemos
uso de seus índices:
Por exemplo:
valor = vetorHibrido[10]
Capítulo 7: Vetores
Lógica de Programação com node.js
vetorHibrido[4] = 7
Capítulo 7: Vetores
Lógica de Programação com node.js
6 }
10 console.log(media)
O exemplo 7.5 retrata como visitar todos os elementos de um vetor do índice 0 até
o índice tamanho - 1.
Isso ocorre por conta do laço e da variável i que é utilizada dentro do laço como
índice de acesso ao vetor (linha 5).
Capítulo 7: Vetores
Lógica de Programação com node.js
Nesse momento do código resta apenas dividir a valor de media pelo tamanho do
vetor (linha 8) de forma que a variável passe a guardar a média dos elementos
contidos no vetor.
Um laço foreach não possui contador, mas sim uma variável temporária que tem
como valor um elemento diferente do vetor a cada iteração do laço.
4
Em outras linguagens esse tipo de laço é chamado de foreach, mas no node.js este termo
é usado para um tipo diferente de laço.
Capítulo 7: Vetores
Lógica de Programação com node.js
7 }
10
11 console.log(media)
Já sabemos que podemos utilizar vetores em conjunto com laços, mas também
podemos utilizá-los em conjunto com subprogramas. Dessa forma, podemos utilizar
vetores como parâmetros, como variáveis locais e também como retorno em
subprogramas.
5
A utilização de um break dentro do foreach o faria parar antes de visitar todos os
elementos.
Capítulo 7: Vetores
Lógica de Programação com node.js
7 }
10
11 return media
12 }
13
15
17
18 console.log(resultado)
No exemplo 7.7 podemos observar como nosso código agora pode ser utilizado
para computar a média de qualquer vetor. Como o algoritmo foi isolado em um
subprograma, agora é possível passar vetores com conteúdo distintos toda vez que
Capítulo 7: Vetores
Lógica de Programação com node.js
fomos utilizar o subprograma. Isso torna o nosso código mais flexível, ajudando na
reusabilidade do mesmo.
Até este capítulo, todas as vezes que passamos um parâmetro para uma função
esse parâmetro se torna uma cópia do valor da variável original:
3 }
7 console.log(valor) // imprime 15
10
O exemplo 7.8 mostra o que acontece toda vez que passamos uma variável de um
tipo primitivo (um número ou um booleano no caso do node.js) como parâmetro de
uma função: a função recebe uma cópia do valor original da variável. E assim, toda vez
que a função altera o parâmetro (linha 2) ela apenas modifica a cópia, mas não a
variável original. É por isso que a linha 11 do exemplo também imprime o valor 15.
Capítulo 7: Vetores
Lógica de Programação com node.js
Assim ao criarmos uma função que recebe um vetor como parâmetro podemos
observar como a passagem por referência se comporta:
3 parametro[1] = 10
4 }
10
No exemplo 7.9 a função alterar recebe um vetor como parâmetro, como vetores
são passados por referência, quando alteramos o parâmetro da função (linha 3) a
variável original também é alterada. Dessa forma, quando o vetor é impresso ao final
do código (linha 12) a saída no console mostra que o índice, que antes tinha o valor
4.5, agora passa a ter o valor 10.
Capítulo 7: Vetores
Lógica de Programação com node.js
O node.js trata Strings como vetores onde cada índice contém um caractere. Por
conta disso, todas as operações básicas sobre vetores também podem ser realizadas
sobre strings. Por exemplo, é possível acessar um caractere usando a sua posição
como índice, é possível utilizar a propriedade length para saber o tamanho da String e
também é possível utilizar laços para percorrer os caracteres de uma string como se
ela fosse um vetor:
4 console.log(string[2]) // imprime l
11 }
Assim, como vetores strings também são objetos e por tanto quando as usamos
como parâmetro funções elas são passadas por referência.
Capítulo 7: Vetores
Lógica de Programação com node.js
6
https://www.w3schools.com/jsref/jsref_obj_regexp.asp
Capítulo 7: Vetores
Lógica de Programação com node.js
let s1 = "carro"
Cria e retorna uma nova
let s2 = "boneca"
concat(string1, string com o conteúdo de
string2) string1 seguido do
// g = "carroboneca"
conteúdo de string2.
let g = s1.concat(s2)
Capítulo 7: Vetores
8. ARRAYS
No capítulo passado vimos como vetores representam coleções ordenadas de
dados. Ou seja, um vetor representa uma lista de dados onde cada elemento possui
um índice.
linha\coluna 0 1 2 3 4
Capítulo 8: Arrays
Lógica de Programação com node.js
Cada array pode conter elementos de qualquer outro tipo, inclusive outro
array;
A propriedade length também aplica-se à arrays;
Acesso e edição aos elementos de um array são feitos utilizando seus
índices.
Por fim, arrays podem ter n dimensões, dessa forma, quando necessário fazermos
uso de arrays de 3, 4 ou mais dimensões.
Porém, o uso de arrays com muitas dimensões pode ser uma má prática de
programação, uma vez que pode tornar o código de difícil compreensão. Há outras
formas de tornar o armazenamento de informações no código mais simples, como a
utilização de classes (assunto do próximo capítulo).
Capítulo 8: Arrays
Lógica de Programação com node.js
4 [4, 1, 4, -8]
5 ]
11 ]
Assim como os vetores, arrays são variáveis como quaisquer outras. Porém, sua
inicialização é feita através de arrays de array. Ou seja, um array de duas dimensões
não passa de um array onde cada um dos seus também são arrays.
Cada elemento do array possui uma espécie de endereço representado por dois
índices: sua linha e sua coluna.
linha\coluna 0 1 2 3
0 1 8 2 0
1 7 3 -2 14
2 4 1 4 -8
Capítulo 8: Arrays
Lógica de Programação com node.js
Tanto para acessar, quanto para editarmos os valores dos elementos de um array
utilizamos seu modelo de indexação através de linhas e colunas:
2 [1, 8, 2, 0],
4 [4, 1, 4, -8]
5 ]
variavel[linha, coluna]
Valores que estiverem fora do intervalo válido serão tratados pelo node.js como
valores undefined.
A edição de elementos de um array também faz uso dos dois índices: linha e
coluna, assim como mostrado no exemplo abaixo:
Capítulo 8: Arrays
Lógica de Programação com node.js
2 [1, 8, 2, 0],
4 [4, 1, 4, -8]
5 ]
1 let arrayDeNumeros = [
2 [1, 8, 2, 0],
Capítulo 8: Arrays
Lógica de Programação com node.js
4 [4, 1, 4, -8]
5 ]
Por fim, para computarmos o número total de elementos do array temos que
somar a quantidade de elementos em cada linha (ver linha 11).
Capítulo 8: Arrays
Lógica de Programação com node.js
1 let arrayDeNumeros = [
2 [1, 8, 2, 0],
4 [4, 1, 4, -8]
5 ]
14 }
15 }
16
O exemplo 8.5 mostra como visitar todos os elementos de um vetor, para entendê-
lo é preciso compreender como os dois laços que ele possui funcionam.
Já o laço que se inicia na linha 10 e vai até a linha 15 é responsável por variar os
valores de i de 0 até a quantidade de linhas do array. Isso faz com que o laço das
linhas 11 até 14 seja executado várias vezes, uma vez para cada linha do array. Ou
seja, o laço da linha 10 até a linha 15 faz com que todas as linhas do array sejam
visitadas.
Capítulo 8: Arrays
Lógica de Programação com node.js
Toda vez que um elemento do array é visitado, seu valor é somado ao valor da
variável media. Mas para evitar que ao final do programa a variável media tenha a
soma dos elementos e não a média deles, ela é dividida pela variável
totalDeElementos.
1 let arrayDeNumeros = [
2 [1, 8, 2, 0],
4 [4, 1, 4, -8]
5 ]
14 }
15 }
16
Capítulo 8: Arrays
Lógica de Programação com node.js
Assim como no exemplo 8.5, o código do exemplo 8.6 utiliza dois laços: uma para
visitar todos os elementos de uma linha e outro para visitar todas as linhas.
2 function mediaArray(array) {
Capítulo 8: Arrays
Lógica de Programação com node.js
10 }
11 }
12
14 return media
15 }
16
17 let arrayDeNumeros = [
18 [1, 8, 2, 0],
19 [7, 3, -2],
20 [4, 1, 4, -8]
21 ]
24 console.log(resultado)
Capítulo 8: Arrays
Lógica de Programação com node.js
Podemos compreender um array de duas dimensões como uma tabela com linhas
e colunas. Já arrays de três dimensões podem ser compreendidos como algo como
um cubo onde temos uma quantidade de linhas (altura), de colunas (largura) e uma
quantidade de páginas (profundidade do array).
2 let array3d = []
Capítulo 8: Arrays
Lógica de Programação com node.js
12 }
13 }
14 }
15
7
https://pt.wikipedia.org/wiki/Hipercubo
Capítulo 8: Arrays
Lógica de Programação com node.js
Capítulo 8: Arrays
9. TIPOS CRIADOS PELO
PROGRAMADOR
Nos capítulos anteriores vimos como guardar e manipular coleções de
informações como notas dos estudantes, notas de matérias, entre outras coisas.
Por exemplo, para realizarmos operações com notas só precisamos das notas em
si (seus valores numéricos), mas e se tivermos a necessidade de trabalharmos dados
que representam conceitos como uma matéria ou um estudante dentro do nosso
programa?
Tipos definidos pelo programador são uma forma de representar as entidades que
existem no mundo real dentro de um programa de computador.
Porém, diferente dos arrays (que associam um índice a cada informação presente
neles), cada informação possui um rótulo associado (representado pela coluna
atributo na tabela 9.1).
Por exemplo, o tipo matéria representa apenas as características que uma matéria
possui. Usando o tipo como base, é possível criar diversas variáveis do tipo matéria.
Por exemplo, a matéria Lógica de Programação (tabela 9.2), a matéria Inglês I ou a
matéria Redes de Computadores.
Atributo Valor
Agora que já sabemos que podemos criar nossos próprios tipos com os atributos
que forem necessários, precisamos compreender que além disso, há outra
característica muito importante que todo tipo possui: as operações que podemos
realizar com os mesmos.
Por exemplo, com o nosso tipo matéria podemos realizar diversas operações
como alterar a ementa, matricular estudantes, vinculá-la a um curso, etc.
As operações que um tipo possui são representadas por funções que atuam sobre
variáveis do próprio tipo.
Por fim, cada tipo pode conter quantos atributos e operações o programador
desejar. E na prática, cada projeto vai necessitar de tipos específicos. Inclusive é
possível criar tipos que utilizam outros tipos criados pelo próprio programador. Por
exemplo, por que o atributo professor do tipo matéria é uma string e não outro tipo
(contendo nome, cpf, telefone, etc.) criado pelo próprio programador?
Assim, vamos utilizar classes todas as vezes que desejarmos criar nossos
próprios tipos no node.js
Nesta seção, vamos adotar como exemplo um conceito bem simples e muito
comum no nosso cotidiano: a criação do tipo Retangulo.
Para criarmos uma classe no node.js vamos utilizar uma função especial chamada
constructor, além de duas palavras reservadas novas: class (que inicia a criação de
um novo tipo) e this (utilizada para acessar os atributos de uma classe dentro do
construtor):
5 }
6 }
10
14
Dentro da classe temos uma função especial chamada constructor. O papel dela
é inicializar os atributos da classe (a base e a altura). Assim, a função recebe como
parâmetros os valores e os guarda nos atributos da classe conforme as linhas 3 e 4.
Nestas linhas também aparece a palavra reservada this como forma de diferenciar
um atributo da classe de uma variável local (um parâmetro).
Agora que já sabemos como criar novos tipos e manipular seus atributos através
de classes, precisamos compreender como criar operações sobre tipos. Para tal,
vamos utilizar o conceito de funções.
5 }
6 }
10 }
11
14 }
15
17
20
De forma geral, toda vez que precisamos criar um tipo novo, devemos sempre
pensar sobre quais serão os atributos do tipo e quais serão as operações que serão
realizadas sobre ele. Só depois, devemos começar a escrever o código do nosso tipo.
Sabemos que classes podem conter atributos de diversos tipos distintos, os tipos
dos atributos de uma classe também podem ser do tipo array do tipo string ou até
mesmo de outras classes.
Para ilustrar essa relação, vamos criar o tipo Estudante com os atributos nome e
matrícula e Turma que possui o semestre de entrada e um vetor de estudantes:
5 }
6 }
12 }
13 }
14
17
20
No exemplo 9.3 são criadas duas classes: Estudante (linhas 1 até 6) e Turma
(linhas 8 até 13). Dentro da classe Turma, a linha 11 representa a ligação entre as duas
classes, uma vez que Turma possui uma coleção (um vetor de Estudante). Note
também que cada elemento do vetor é um objeto do tipo Estudante.
Sobre a relação entre strings e classes das strings, estudamos que strings podem
ser tratadas como arrays no node.js. Contudo, há dois tipos de Strings: as primitivas
(exemplo: let sp = "Allan") e as que são objetos (exemplo: let so = new
String("Allan")).
Strings primitivas são do tipo "string" (typeof de uma variável desse tipo
retorna "string"), já as strings do tipo objeto são implementadas por uma classe
chamada String (typeof de uma variável desse tipo retorna "object").
Os exemplos 9.4 e 9.5 mostram como separar nossas classes Estudante e Turma
em um arquivo para cada:
5 }
6 }
9 exports.Estudante = Estudante
8 }
9 }
10
14
17
O exemplo 9.5 começa importando o código da classe Estudante (linha 2): const
{Estudante} = require('./Estudante.js'). Assim, será possível declarar objetos
do tipo Estudante dentro do arquivo como se a classe estivesse declarada no próprio
(linhas 12 e 13).
Funções Recursivas
1 x 2 x 3 x 4 x 5 = 120
O que caracteriza a função fatorial como uma função recursiva é justamente o fato
de utilizarmos a própria definição da função para calculá-la: fatorial(n) = n *
fatorial(n-1).
5 resultado = resultado * i
6 }
7 return resultado
8 }
11
O exemplo 10.1 utiliza uma solução tradicional que vamos chamar durante o
capítulo de iterativa.
Nela é possível observar o código que o código da função fatorial possui um laço
(linhas 4 a 6) onde o contador i que começa com o valor de n e vai sendo
decrementado de um em um até chegar em 1. Consequentemente os valores que i
assume são n, n-1, n-2, ... 1, ou seja, justamente os valores que nós
precisamos multiplicar para chegarmos ao fatorial do número n.
2 if (n == 0 || n == 1) { // casos base: n = 0 ou n = 1
3 return 1
4 }
7 }
10
A chamada recursiva da linha 6 faz com que toda vez que chamemos a função
ocorra uma sequência de novas chamadas da função, cada uma com um número que
é uma unidade inferior a chama anterior.
Toda função recursiva quando executada tem essa característica de gerar uma
sequência de chamadas a ela mesma que a se chegue em um dos casos base.
Quando uma das chamadas entra em um caso base, ela retorna, gerando assim
uma sequência de retornos até voltar à chamada original e por sua vez encerra a
recursão retornando 120.
Quando a função nunca chega no caso base, ela entra em recursão infinita
(nunca para até a pilha de chamada do interpretador estoure o seu limite de
memória o que encerra o programa);
É possível haver mais uma chamada recursiva dentro da mesma função, o
cálculo dos números de Fibonacci é um ótimo exemplo disto;
Algumas soluções recursivas são muito mais simples do que suas versões
iterativas, mais uma vez, o cálculo dos números de Fibonacci é um ótimo
exemplo disto;
De forma geral, laços podem ser trocados por funções recursivas, assim
é possível utilizar recursão para implementar programas para problemas
como imprimir números em sequência, computar a média de um array, e
basicamente qualquer outro problema que envolva laços.