Você está na página 1de 134

INSTITUTO FEDERAL DE EDUCAÇÃO, CIÊNCIA E TECNOLOGIA DE PERNAMBUCO

Reitor

JOSÉ CARLOS DE SÁ

Conselho Editorial

Presidente

Este livro é resultado do projeto de extensão Democratização ao Acesso aos Cursos


de Tecnologia da Informação: Uma ponte entre o IFPE - Campus Igarassu e a
Sociedade Local com as respectivas bolsistas Joanna Victória Batista da Silva e
Rayane Sales Santos. Diagramadoras, editoras e revisoras.
SUMÁRIO
1. ALGORITMOS ....................................................................................................... 11
1.1. Entradas e saídas de um algoritmo ............................................................ 12
1.2. Níveis de abstração ..................................................................................... 13
1.3. Algoritmos e Programas de Computadores .............................................. 13
2. OPERADORES, TIPOS E VARIÁVEIS.................................................................... 18
2.1. Números, Textos e Tipos ............................................................................. 18
2.1.1. Observações sobre o tipo number no node.js ........................................ 19
2.1.2. Observações importantes sobre o tipo string no node.js...................... 19
2.2. Operadores ................................................................................................... 20
2.2.1. Principais Operadores Aritméticos do tipo number .............................. 20
2.2.2. Operador de concatenação para Strings ................................................ 21
2.2.3. Expressões Ambíguas ............................................................................. 22
2.3. Variáveis ....................................................................................................... 23
3. EXECUÇÃO CONDICIONAL.................................................................................. 28
3.1. Bloco if … else ............................................................................................... 29
3.2. Bloco if ... else if ... else ............................................................................... 32
3.3. Bloco switch ... case .................................................................................... 34
3.4. Quando utilizar cada tipo de bloco? ........................................................... 38
4. OPERADORES LÓGICOS ...................................................................................... 45
4.1. Negação no node.js ..................................................................................... 46
4.1.1. Negação e Execução Condicional........................................................... 47
4.2. Conjunção no node.js .................................................................................. 48
4.2.1. Conjunção e Execução Condicional........................................................ 49
4.3. Disjunção no node.js .................................................................................... 51
4.3.1. Disjunção e execução Condicional ......................................................... 52
4.4. Utilização da Negação, Conjunção e Disjunção ao Mesmo Tempo ........ 54
5. LAÇOS ................................................................................................................... 57
5.1. while .............................................................................................................. 59
5.2. for .................................................................................................................. 61
5.3. do ... while ..................................................................................................... 65
5.4. while, for ou do … while qual utilizar? ......................................................... 68
5.5. Laços Aninhados .......................................................................................... 69
5.6. Laços infinitos e como evitá-los ................................................................. 72
6. SUBPROGRAMAS ................................................................................................. 76
6.1. Subprogramas Procedimentais .................................................................. 77
6.2. Subprogramas Funcionais........................................................................... 80
6.3. Quando utilizar subprogramas funcionais e quando subprogramas
procedimentais? .............................................................................................................. 82
6.4. Escopo, variáveis globais e variáveis locais .............................................. 82
7. VETORES .............................................................................................................. 90
7.1. Declaração e Inicialização de Vetores........................................................ 91
7.2. Acessando e Editando Vetores ................................................................... 93
7.3. Tamanho de um Vetor ................................................................................. 94
7.4. Vetores e Laços ............................................................................................ 95
7.5. Vetores e Subprogramas ............................................................................. 97
7.5.1. Passagem por valor e passagem por referência ................................... 99
7.6. Vetores e Strings ........................................................................................ 100
7.6.1. Funções úteis para Strings .................................................................... 101
8. ARRAYS .............................................................................................................. 105
8.1. Declaração e Inicialização de Vetores...................................................... 106
8.2. Acessando e Editando Arrays ................................................................... 107
8.3. Tamanho de um Array ............................................................................... 109
8.4. Arrays e Laços ............................................................................................ 110
8.5. Arrays e Subprogramas ............................................................................. 113
8.6. Arrays de Três ou mais Dimensões .......................................................... 114
9. TIPOS CRIADOS PELO PROGRAMADOR .......................................................... 119
9.1. Criando tipos .............................................................................................. 120
9.2. Criando operações sobre tipos ................................................................. 123
9.3. Relação entre Classes, Arrays e Strings ................................................... 124
9.4. Separando nosso código em arquivos ..................................................... 126
RECURSÃO .................................................................................................................. 130
Funções Recursivas ................................................................................................ 130
Ao meu amado tio Manoel Marques da Silva Neto.

Que a memória das centenas de milhares de brasileiros mortos durante a pandemia da


COVID19 não seja esquecida. A vida do meu tio, assim como a da maioria dos brasileiros,
poderia ter sido preservada se o negacionismo científico não tivesse sido predominante
por parte de nossos dirigentes.
1. ALGORITMOS
No nosso dia-a-dia frequentemente utilizamos listas de passos para resolver
problemas ou realizar ações. Por exemplo, quando usamos uma receita para fazer
comida ou quando encontramos na internet a solução para um problema que tenha
ocorrido nos nossos computadores.

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?

Para resolver esse problema vamos montar um conjunto de passos mostrando


como realizar a conexão:

Exemplo 1.1 - Como conectar um celular à internet.

1 Abra o aplicativo de configurações do celular;

2 Localize a opção “Wi-Fi”;

3 Clique na opção “Wi-Fi”;

4 Localize pelo nome a rede que você quer conectar-se;

5 Clique no nome da rede que você quer conectar-se;

6 Digite a senha da rede;

7 Clique em conectar.

O exemplo 1.1 representa um algoritmo que quando executado permite a uma


pessoa conectar seu celular à internet.

Mas o que seria um algoritmo?

Um algoritmo nada mais é do que um conjunto de passos que executamos


sequencialmente para resolver um problema específico.

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

1.1. Entradas e saídas de um algoritmo

Como vimos anteriormente, a principal parte de um algoritmo é o seu conjunto de


passos. Porém, há duas outras partes muito importantes: os itens necessários para a
sua execução (suas entradas) e o resultado esperado (suas saídas).

Os itens necessários para a execução são informações, materiais, objetos, enfim,


tudo aquilo que será utilizado durante a execução do algoritmo. No futuro, vamos nos
referir a eles como as entradas dos nossos programas. No exemplo 1.1, há dois itens
necessários para a sua execução: o celular a ser conectado e a senha da internet.

Já o resultado esperado, é aquilo que o algoritmo produz após a sua execução. No


futuro, vamos nos referir a eles com as saídas dos nossos programas. No exemplo
1.1, o resultado esperado é que o celular esteja conectado à internet.

Com conceitos de entradas e saídas definidos, podemos reescrever o exemplo 1.1


de forma mais completa:

Exemplo 1.1 - Como conectar um celular à internet.

Entradas

1 Celular desconectado à rede sem fio;

2 Senha da rede sem fio.

Passos

1 Abra o aplicativo de configurações do celular;

2 Localize a opção “Wi-Fi”;

3 Clique na opção “Wi-Fi”;

4 Localize o nome da rede que você quer conectar-se;

5 Clique no nome da rede que você quer conectar-se;

6 Digite a senha da rede;

7 Clique em conectar.

Saídas

1 Celular conectado à internet.

Capítulo 1: Algoritmos
Lógica de Programação com node.js

Compreender as entradas e as saídas de um algoritmo é fundamental antes


mesmo de começarmos a escrevê-lo. As entradas definem aquilo que o algoritmo
necessita para ser executado. Enquanto isso, as saídas descrevem o estado final
daquilo que o algoritmo vai gerar ou modificar durante a sua execução.

De posse dessas informações, o programador é capaz de compreender melhor o


problema que o algoritmo resolve, além de planejar e executar os testes necessários
para garantir que o algoritmo funcione conforme esperado (funciona corretamente).

1.2. Níveis de abstração

Quando escrevemos algoritmos temos que decidir o nível de detalhamento que


iremos utilizar nos passos que iremos descrever. Para isso precisamos escolher o
nível de abstração das nossas instruções.

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.

Já instruções de alto nível de abstração são aquelas que representam várias


ações com uma única instrução. Por exemplo, para abrir uma porta, poderíamos
simplesmente utilizar a seguinte instrução: abra a porta. Esta instrução compreenderia
todos os passos descritos no parágrafo anterior.

Níveis de abstração são muito importantes para compreendermos não só


algoritmos, mas também programas de computadores, algo que veremos na próxima
seção.

1.3. Algoritmos e Programas de


Computadores

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.

Nesse sentido, podemos afirmar que um programa de computador também é um


algoritmo, mas nem todo algoritmo é um programa de computador. Os programas que

Capítulo 1: Algoritmos
Lógica de Programação com node.js

utilizamos no nosso cotidiano como redes sociais, navegadores, editores de textos,


etc. são todos escritos em alguma linguagem de programação e executados pelo
computador.

Nós, seres humanos, compreendemos linguagens como Português, Inglês,


Francês, etc. Já os computadores, compreendem linguagens de um tipo bem
específico conhecidas como linguagens de máquina (assembly em inglês).

Linguagens de máquina lidam diretamente com o hardware do computador


(unidades de computação, registradores, memória, etc.). Para um ser humano,
algoritmos escritos em linguagem de máquina são muito difíceis de compreender por
serem escritos em um nível de abstração muito baixo, ou seja, é necessário escrever
várias instruções para realizar operações simples como, por exemplo, uma soma.

Como forma de facilitar a programação de computadores, nasceram as primeiras


linguagens de programação, elas nos permitem criar instruções de forma muito mais
próxima da língua inglesa do que as linguagens de máquina. Ou seja, são linguagens
de mais alto nível de abstração.

Contudo, para que um programa em uma linguagem de programação seja


executado é necessário utilizarmos outro programa que pode ser um compilador ou
um interpretador.

Um compilador traduz o código em linguagem de programação para linguagem de


máquina gerando um arquivo executável. E para executarmos o nosso programa
precisamos executar o arquivo gerado pelo compilador.

Enquanto isso, um interpretador executa o programa traduzindo suas instruções


para linguagem de máquina durante a execução. Ou seja, o programa é traduzido e
executado toda vez que executamos o interpretador. Isso faz com que normalmente
um código interpretado seja mais lento do que um código já compilado.

Apesar da diferença entre um algoritmo escrito em português e um programa


escrito em uma linguagem de programação, os conceitos fundamentais de entradas,
instruções e saídas também estão presentes na programação. Ou seja, um programa
de computador também possui um conjunto de passos que são executados
sequencialmente, esses passos processam os dados de entrada para gerar as saídas.

O exemplo 1.2 mostra como essa estrutura também está presente em um


programa de computador. O exemplo é bem simples, apenas somar dois números,
mas notem como o programa começa com as entradas (linhas 1 e 2), depois temos os
passos (na verdade apenas um passo: a linha 4) e terminamos gerando uma saída na
tela do computador (linha 6). Caso não seja possível entender todos os detalhes do
código, não há razão para preocupar-se, afinal estudaremos os detalhes desses
programas nos próximos capítulos.

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

4 resultado = numeroA + numeroB // algoritmo em si (passos)

6 console.log(resultado) // saída (imprime a soma na tela do


computador)

Além da estrutura de um algoritmo, um programa de computador também possui


outros paralelos com algoritmos escritos na língua portuguesa, duas dessas
semelhanças são os conceitos de execução condicional e de laços.

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:

se tiver uma máquina de lavar, utilize a máquina para lavar a camisa

caso contrário, lave a camisa manualmente.

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.

Laços também são um conceito muito popular em algoritmos e em programas de


computador. Eles representam a repetição de um conjunto de instruções até que uma
condição torne-se falsa. Por exemplo, quando vamos lavar uma panela precisamos:

esfregar a bucha enquanto houver sujeira na panela.

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).

Agora que compreendemos o conceito de um algoritmo e que compreendemos


que programas de computador também são algoritmos, podemos começar a estudar
os programas em si (como o exemplo 1.2). Começaremos com programas simples,
que resolvem problemas relativamente triviais. Mas no decorrer dos capítulos vamos
evoluir a complexidade do nosso código aos poucos. Assim, estudaremos execução
condicional, laços e vários outros conceitos fundamentais para escrevermos bons
programas.

Capítulo 1: Algoritmos
2. OPERADORES, TIPOS E VARIÁVEIS

2.1. Números, Textos e Tipos

Na língua portuguesa há dois elementos que são utilizados constantemente: os


textos e números.

Os textos são compostos por cadeias de caracteres. Por exemplo, o texto "bola" é
composto pelos caracteres 'b', 'o', 'l' e 'a'.

Já os números são divididos em duas categorias básicas: números inteiros e


números de ponto flutuante (números reais). Por exemplo, o número 5 é um número
inteiro, enquanto que o número 3,14 é um número de ponto flutuante.

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.

Números e textos são partes fundamentais no node.js, como em qualquer outra


linguagem de programação. Eles representam dois exemplos daquilo que chamamos
como tipos.

De forma geral, um tipo representa uma categoria de dados dentro de uma


linguagem de programação. Ou seja, uma forma de categorizar as informações com
as quais lidamos dentro dos nossos programas.

Há maneiras formais (matemáticas) de definirmos tipos, entretanto esse nível de


profundidade é mais adequado para livros sobre Paradigmas de Linguagens de
Programação, ou Compiladores.

Neste capítulo, vamos abordar dois tipos fundamentais no node.js: números e


textos.

Dentro do node.js o tipo número é chamado de number, já o tipo texto é chamado


de string.

A tabela abaixo apresenta uma visão geral desses dois tipos na linguagem.

Exemplos no
Tipo Dados que representa
Node.js

Número de forma geral, incluindo números inteiros 5


Number positivos, números inteiros negativos e números decimais -4
(de ponto flutuante). 10.14

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

'a'
"a"
String Cadeias de caracteres
'Abacate'
"abacate"
Tabela 2.1 - Visão geral dos tipos number e string.

2.1.1. Observações sobre o tipo number no


node.js

Diferente de outras linguagens, no node.js não há uma separação (tipos distintos)


para representar números inteiros e números de ponto flutuante.

Além disso, na língua portuguesa, quando queremos representar um número de


ponto flutuante utilizamos a vírgula como forma de separar a parte inteira da parte não
inteira, exemplo: 3,14. Entretanto, no node.js e na maioria das linguagens de
programação utilizamos o ponto como separador, exemplo: 3.14.

2.1.2. Observações importantes sobre o tipo


string no node.js

Diferente de outras linguagens, no node.js não há uma separação (tipos distintos)


para representar caracteres e palavras. Contudo, é importante ressaltar que em outras
linguagens muito populares as aspas simples são utilizadas para representar
caracteres e as aspas duplas para strings, ou seja, 'a' seria um caractere e "a" uma
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.

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

2.2. Operadores

Assim como na matemática, em linguagens de programação também utilizamos


operadores para realizar determinadas ações.

Cada tipo tem um conjunto de operadores que podem ser aplicados a eles, como
veremos a seguir.

2.2.1. Principais Operadores Aritméticos do tipo


number

No node.js e demais linguagens de programação, os operadores que podem ser


utilizados com números são muito parecidos com os operadores presentes na
matemática.

Assim, operadores conhecidos como +, -, / entre outros, são utilizados conforme a


tabela abaixo.

Exemplo no node.js

Nome do Operador Símbolo no node.js


Expressão Resultado

Soma + 4+5 9

Subtração - 10 - 3 7

Multiplicação * 2*6 12

Divisão / 8/2 4

Resto da Divisão Inteira % 12 % 5 2

Exponenciação ** 5**2 25
Tabela 2.2 - Principais operadores aritméticos do tipo number.

Os operadores da soma e subtração são idênticos no símbolo e no funcionamento


ao que estamos acostumados a utilizar na matemática.

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

O operador do resto da divisão inteira normalmente gera um pouco de confusão na


cabeça de um programador iniciante. Mas o que ele faz é relativamente simples:
computa o resto da divisão inteira entre dois números.

O resultado da expressão a % b é a subtração entre o primeiro operando (a) e a


computação da multiplicação entre o segundo operando (b) e o resultado da divisão
inteira entre o primeiro (a) e o segundo (b) operandos, ou seja:

a % b = a - (b * (resultado da divisão inteira de a por b))

Expressão Resultado da Divisão Inteira Resto / Resultado da Expressão

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 %.

Por fim, é importante não confundir o operador para a exponenciação ** com o


operador ^ que também existe na linguagem, mas funciona como um operador lógico
executando um “ou exclusivo bit-a-bit” (mais sobre o assunto em capítulos futuros).

2.2.2. Operador de concatenação para Strings

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 +.

Basicamente, o que o operador + realiza quando aplicado a strings é a junção de


ambas, como exemplificado na tabela abaixo.

Expressão Resultado

"bola" + "azul" "bolaazul"

"bola " + "azul" "bola azul"

"bola" + " " + "azul" "bola azul"

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

'bola' + "azul" "bolaazul"


Tabela 2.4 - Exemplos de uso do operador + para strings.

Também é possível utilizar o operador + em conjunto com strings e números como


operandos tendo como resultado uma string.

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.

2.2.3. Expressões Ambíguas

Assim como na matemática, linguagens de programação permitem o uso de


expressões ambíguas. Por exemplo, qual seria o valor da expressão abaixo?

10 + 5 * 2

Há dois resultados possíveis: 30 (quando realizamos primeiro a soma) e 20


(quando realizamos primeiro a multiplicação).

Para evitar ambiguidades, linguagens de programação utilizam tabelas de


precedência de operadores1 indicando qual operador tem preferência na ordem de
execução em expressões ambíguas.

Assim, no caso da expressão 10 + 5 * 2, executada por um programa em node.js,


teríamos 20 como resultado porque a multiplicação tem precedência sobre a soma.

Entretanto, nesse caso, a melhor forma de evitarmos problemas de interpretação


de código é escrevermos expressões que não são ambíguas utilizando parênteses,
como na matemática. Por exemplo, poderíamos escrever (10 + 5) * 2 caso fosse
necessário realizar primeiro a soma, ou 10 + (5 * 2) caso fosse necessário realizar
primeiro a multiplicação.

1
Operator precedence - JavaScript _ MDN: https://developer.mozilla.org/en-
US/docs/Web/JavaScript/Reference/Operators/Operator_Precedence

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

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.

Uma variável é um nome ou rótulo que utilizamos para representar um espaço na


memória do programa. Além disso, as variáveis são voláteis, ou seja, só existem
enquanto o programa está sendo executado pelo sistema operacional.

Na matemática utilizamos algo análogo a variáveis em expressões como y = x + 2,


onde há duas "variáveis" x e y que podem assumir os valores que desejarmos. Por
exemplo, quando x = 2, o resultado de y seria 4.

Mas como traduzir esse conceito para o node.js?

Para tal, vamos trabalhar na criação de um programa capaz de computar o


perímetro de um quadrado.

Imagem 2.1 - representação gráfica de um quadrado.

Para resolvermos esse problema é necessário compreendê-lo melhor.

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.

Para representarmos o valor do lado do quadrado vamos precisar de uma variável,


que neste exemplo será chamada lado. A variável lado pode assumir qualquer valor
válido, podemos dar um valor arbitrário a ela durante o código, ou pedirmos para o
usuário do programa informar um valor (algo que será visto em capítulos futuros).

Além disso, precisamos compreender como computar o perímetro do quadrado. A


fórmula do perímetro do quadrado é a soma dos seus quatro lados. Assim, há duas
possíveis expressões capazes de realizar esta operação:

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

Possível expressão #1: lado + lado + lado + lado

Possível expressão #2: 4 * lado

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.

Por fim, precisamos também de uma variável para representar o resultado da


nossa expressão (valor computado do perímetro), neste caso vamos chamá-la de
perimetro (sem o acento, uma vez que não é o ideal usar acentos em códigos, na
verdade, não é o ideal escrever código em outra língua que não seja a inglesa, mas
isso é assunto para outro momento).

Assim, a primeira versão da nossa solução para o problema ficaria assim:

Exemplo 2.1 - Computa o perímetro de um quadrado.

1 lado = 10

2 perimetro = 4 * lado

Vamos compreender o código?

Primeiro, precisamos entender um operador novo, o = conhecido como operador


da atribuição. Ele nos permite atribuir um valor a uma variável, por exemplo y = 7,
significa que a partir dessa linha o valor de y passa ser 7 dentro do programa.

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.

Assim, a segunda versão da nossa solução para o nosso problema ficaria da


seguinte forma:

Exemplo 2.2 - Código que computa o perímetro de um quadrado com comentários.

1 lado = 10 // atribui o valor 10 à variável lado

2 perimetro = 4 * lado // computa o perímetro e guarda em


perimetro

Há diversas formas de escrever comentários em um código, mas dessa vez


adotamos a mais simples, as barras duplas: //.

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

Outra maneira de visualizar o nosso programa é através de uma tabela mostrando


o valor das variáveis no decorrer da execução do programa, linha após linha:

Linha Código Valor de lado Valor de perimetro

1 lado = 10 10 variável inexistente

2 perimetro = 4 * lado 10 40

Tabela 2.6 - Valores das variáveis durante a execução do programa.

Assim, conforme a nossa tabela, ao final do nosso programa o valor da variável


perimetro é 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.

Assim, nossa terceira e última versão do código ficaria assim:

Exemplo 2.3 - Código que computa o perímetro de um quadrado com comentários.

1 lado = 10 // atribui o valor 10 à variável lado

2 perimetro = 4 * lado // computa o perímetro e guarda em


perimetro

3 console.log(perimetro) // imprime o valor da variável perimetro


na tela

Para compreendermos melhor o impacto na adição de uma nova linha de código


ao nosso programa vamos voltar a tabela de execução linha-a-linha do nosso código:

Linha Código Valor de lado Valor de Tela do


perimetro PC

1 lado = 10 10 variável vazia


inexistente

2 perimetro = 4 * lado 10 40 vazia

3 console.log(perimetro) 10 40 40

Capítulo 2: Operadores, Tipos e Variáveis


Lógica de Programação com node.js

Tabela 2.7 - Valores das variáveis e saída do console durante a execução do


programa.

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

Imagem 2.2 - Resultado da execução do programa perimetro.js

Ao final do nosso programa o resultado impresso na tela do console é 40,


conforme nós havíamos planejado.

Capítulo 2: Operadores, Tipos e Variáveis


3. EXECUÇÃO CONDICIONAL
No dia a dia somos levados a tomar decisões entre duas ou mais possibilidades,
por exemplo:

 Decisões com duas possibilidades


o Vamos fazer um bolo de milho ou de cenoura?
o Vamos lavar a roupa com sabão líquido ou em pó?
o ...
 Decisões com mais de duas possibilidades
o Em qual dia da semana vou fazer a faxina na casa?
o Qual matéria vou estudar hoje?
o ...

Quando confrontados com essas decisões nós normalmente escolhemos a opção


que nos parece mais lógica, por exemplo:

 Vamos lavar a roupa com sabão líquido ou em pó?


o Se a lavagem será na máquina, é melhor usar sabão líquido;
o Se a lavagem será manual, é melhor usar sabão em pó;
o …

De forma análoga, quando escrevemos programas de computador,


frequentemente somos confrontados com decisões que precisam ser tomadas de
forma lógica durante a execução do programa, por exemplo:

 Só vamos continuar a execução do código se os dados fornecidos pelo


usuário forem válidos;
 Se for o aniversário do usuário, envie uma mensagem de parabéns;
 Quando estiver perto do prazo de entrega do exercício, envie um e-mail para
todos os estudantes;
 ...

Assim, é possível visualizar esse tipo de execução condicional da seguinte forma:

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

Imagem 3.1 - Exemplo de execução condicional em um programa.

Dentro da língua portuguesa, quando queremos escrever um texto com uma


condição, comumente utilizamos expressões se e caso contrário. Já quando
programamos, utilizamos os termos equivalentes na língua inglesa: o if que significa
se; e o else que significa caso contrário.

3.1. Bloco if … else

Uma das formas mais simples e intuitivas de uma execução condicional é a


utilização da estrutura condicional if … else. Nela o nosso programa é capaz de
escolher entre duas opções de caminhos distintos durante a execução.

A estrutura de um código com if é relativamente simples e intuitiva:

if (condição) {
Código executado quando a condição é verdadeira
} else {
Código executado quando a condição é falsa
}

No modelo acima é importante observar que quando a condição é verdadeira


(true), o programa executa a sequência de linhas demarcadas pelas chaves de início {
e de fim } do if. Entretanto, quando a condição é falsa (false), as linhas de código que
são executadas são outras e demarcada pelas chaves de início { e de fim } do else.

Além disso, o bloco else é opcional, devendo ser utilizado apenas quando
necessário.

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

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.

Vamos ver agora como podemos aplicar a construção if … else na solução de um


problema: como identificar se um número é positivo.

Nossa solução será construída a partir de uma estrutura simples, mas que será
melhorada no decorrer deste capítulo.

Solução #1:

Exemplo 3.1 - Testa se um número é positivo

1 var numero = 6 // o número a ser testado

3 // imprime o número a ser testado

4 console.log("numero a ser testado:", numero)

6 if (numero > 0) { // testa se o número eh maior que zero

7 // a linha 8 é executada quando a condição do if for


verdadeira

8 console.log("o numero testado eh positivo")

9 }

10 // notem que a linha abaixo é sempre executada por estar fora


do if

11 console.log("fim do programa")

Qual a saída no terminal ao executarmos nossa primeira solução?

numero a ser testado: 6


o numero testado eh positivo
fim do programa

Ou seja, como a expressão numero > 0 tem como resultado true, a linha 8 do
programa é executada.

Porém, se modificarmos o valor da variável numero na linha 1 do programa para -4,


a saída seria no console seria:

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

numero a ser testado: -4


fim do programa
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.

Nossa primeira solução é capaz de identificar se um número é positivo, mas


quando o nosso teste de positividade (linha 6) resulta em false, o programa
simplesmente não faz nada. Assim, nossa solução pode ser melhorada através da
utilização de um else:

Solução #2:

Exemplo 3.2 - Testa se um número é positivo ou não

1 var numero = 6 // o numero a ser testado

3 // imprime o numero a ser testado

4 console.log("numero a ser testado:", numero)

6 if (numero > 0) { // testa se o numero eh maior que zero

7 // a linha 8 eh executada quando a condicao do if for


verdadeira

8 console.log("o numero testado eh positivo")

9 } else {

10 console.log("o numero testado nao eh positivo")

11 }

12

13 console.log("fim do programa")

Qual a saída no console ao executarmos nossa segunda solução?

numero a ser testado: 6


o numero testado eh positivo
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.

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

Porém, se modificarmos o valor da variável numero na linha 1 do programa para -4,


a saída seria no console seria:

numero a ser testado: -4


o numero testado nao eh positivo
fim do programa

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.

3.2. Bloco if ... else if ... else

Na seção anterior estudamos como implementar um programa que precisa realizar


uma escolha entre duas opções. Porém, é muito comum que nossos programas
tenham que realizar escolhas entre mais de duas opções. Dessa forma, um simples
bloco if … else não seria suficiente. Para resolver este problema vamos conhecer
agora o bloco if … else if ... else:

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.

Apesar do modelo acima possuir apenas duas condições, o número de condições


indicadas por else if’s é determinado pelo programador.

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:

Exemplo 3.3 - Converte um número para uma string

1 var numero = 6 // o numero a ser convertido

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

3 // imprime o numero a ser testado

4 console.log("numero a ser convertido:", numero)

6 if (numero == 0) { // testa se o numero eh igual a 0

7 console.log("zero")

8 } else if (numero == 1) { // testa se o numero eh igual a 1

9 console.log("um")

10 } else if (numero == 2) { // testa se o numero eh igual a 2

11 console.log("dois")

12 } else if (numero == 3) { // testa se o numero eh igual a 3

13 console.log("tres")

14 } else if (numero == 4) { // testa se o numero eh igual a 4

15 console.log("quatro")

16 } else if (numero == 5) { // testa se o numero eh igual a 5

17 console.log("cinco")

18 } else if (numero == 6) { // testa se o numero eh igual a 6

19 console.log("seis")

20 } else if (numero == 7) { // testa se o numero eh igual a 7

21 console.log("sete")

22 } else if (numero == 8) { // testa se o numero eh igual a 8

23 console.log("oito")

24 } else if (numero == 9) { // testa se o numero eh igual a 9

25 console.log("nove")

26 } else {

27 console.log("numero desconhecido")

28 }

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

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

var numero = 6 // o numero a ser convertido

A única condição verdadeira seria a da linha 18, executando assim a linha 19 e


gerando como saída no terminal:

numero a ser convertido: 6


seis
fim do programa

Quando

var numero = 0 // o numero a ser convertido

A única condição verdadeira seria a da linha 6, executando assim a linha 7 e


gerando como saída no terminal:

numero a ser convertido: 0


zero
fim do programa

Quando

var numero = 15 // o numero a ser convertido

Nenhuma das condições é verdadeira, fazendo o código cair no else presente na


linha 26, executando assim a linha 27 e gerando como saída no terminal:

numero a ser convertido: 15


numero desconhecido
fim do programa

3.3. Bloco switch ... case

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

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.

Um bloco switch … case possui o seguinte formato:

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

1 var numero = 6 // o numero a ser convertido

3 // imprime o numero a ser testado

4 console.log("numero a ser convertido:", numero)

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

6 switch (numero) {

7 case 0: // caso numero for igual a 0

8 console.log("zero")

9 break

10 case 1: // caso numero for igual a 1

11 console.log("um")

12 break

13 case 2: // caso numero for igual a 2

14 console.log("dois")

15 break

16 case 3: // caso numero for igual a 3

17 console.log("três")

18 break

19 case 4: // caso numero for igual a 4

20 console.log("quatro")

21 break

22 case 5: // caso numero for igual a 5

23 console.log("cinco")

24 break

25 case 6: // caso numero for igual a 6

26 console.log("seis")

27 break

28 case 7: // caso numero for igual a 7

29 console.log("sete")

30 break

31 case 8: // caso numero for igual a 8

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

32 console.log("oito")

33 break

34 case 9: // caso numero for igual a 9

35 console.log("nove")

36 break

37 default:

38 console.log("numero desconhecido")

39 } // fim do bloco switch … case

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

var numero = 6 // o numero a ser convertido

A única condição verdadeira seria a da linha 25, executando assim a linha 26 e


posteriormente a linha 27 (um break) encerrando a execução do bloco switch … case e
gerando a seguinte saída no terminal:

numero a ser convertido: 6


seis
fim do programa

Quando

var numero = 0 // o numero a ser convertido

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

A única condição verdadeira seria a da linha 7, executando assim a linha 8 e


posteriormente a linha 9 (um break) encerrando a execução do bloco switch … case e
gerando a seguinte saída no terminal::

numero a ser convertido: 0


zero
fim do programa

Quando

var numero = 15 // o numero a ser convertido

Nenhuma das condições é verdadeira, fazendo o código cair no default presente


na linha 37, executando assim a linha 38 e gerando como saída no terminal:

numero a ser convertido: 15


numero desconhecido
fim do programa

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.

3.4. Quando utilizar cada tipo de bloco?

A dúvida mais comum entre os estudantes iniciantes na programação é a


diferença entre dois tipos de execução condicional: múltiplos if’s seguidos ou bloco do
tipo if … else if.

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.

Então vamos começar estudando a sintaxe de um código com múltiplos if’s:

if (condição1) {
Código executado quando condição1 é verdadeira
}

// os if’s são independentes, não há relação entre eles

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

if (condição2) { // sempre é testada independente do resultado da


condição1
Código executado quando a condição2 é verdadeira
}

O tipo de construção acima pode ser utilizado na resolução de diversos tipos de


problemas. Nesta seção, vamos explorar o seguinte problema: testar se um número é
maior que 10 ou par.

Porém, antes de escrevermos a resposta vamos compreender como o código deve


funcionar:

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

2 Sim Não maior que 10

3 Não Sim par

4 Não Não (nada precisaria ser impresso)

Esse problema pode ser resolvido através do seguinte código:

Exemplo 3.5 - testa se um número é maior que 10 ou par

1 var numero = 15 // o número a ser testado

3 if (numero > 10) { // testa se o número é maior que 10

4 console.log("maior que 10")

5 }

7 if (numero % 2 == 0) { // testa se o número é par

8 console.log("par")

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

9 }

10

11 console.log("fim do programa")

No exemplo 3.5, tanto a condição do primeiro if (linha 3), quanto a condição do


segundo if (linha 7) sempre são testadas independente do resultado de cada um. Isto
ocorre porque ambos os if’s são independentes entre si, ou seja, fazem parte de dois
blocos de código distintos.

Agora vamos estudar o que ocorreria se a resposta para o problema fosse


construída com um bloco if … else if:

Exemplo 3.6 - testa se um número é maior que 10 ou par

1 var numero = 15 // o número a ser testado

3 if (numero > 10) { // testa se o número é maior que 10

4 console.log("maior que 10")

5 // o if abaixo só é testado quando a condição anterior (linha


3) falha

6 } else if (numero % 2 == 0) {

7 console.log("par")

8 }

10 console.log("fim do programa")

No exemplo 3.6, a condição do segundo if (linha 5) só é testada quando a condição


do primeiro if (linha 3) é falsa. Isto ocorre porque ambos os if’s são dependentes entre
si, ou seja, fazem parte de um único bloco de código. Nesse código, a palavra
reservada else é responsável por conectar os if’s em um só bloco de código.

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):

Caso de teste Entrada (valor do número) Saída no console

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

É maior que 10 É par

1 Sim Sim maior que 10

2 Sim Não maior que 10

3 Não Sim par

4 Não Não (nada precisaria ser impresso)

Agora que abordamos a principal dificuldade entre os estudantes iniciantes em


lógica da programação, podemos realizar um resumo de como utilizar cada bloco de
execução condicional estudado neste capítulo:

Quadro 3.2 - Como utilizar as principais formas de execução condicional.


if

Quando Quando há um único teste a ser feito


usar

Exemplo Encontrar o máximo entre dois números

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)

Exemplo Testar se um número é maior que 10 e se é par.

Código var a = 10

if (a > 10) {
console.log("maior que 10")
}

if (a % 2 == 0) {
console.log("par")
}

if … else

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

Quando Quando há dois testes excludentes (se um ocorrer, o outro não ocorre) a
usar serem feitos

Exemplo Testar se um número é par ou ímpar

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

Quando Quando há vários testes a serem feitos em sequência


usar

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.

Código var nota = 10

if (nota >= 6) {
console.log("aprovado")
} else if (nota < 4) {
console.log("reprovado direto")
} else {
console.log("recuperação")
}

switch … case

Quando Quando é preciso comparar se o valor de uma variável é igual a diversos


usar outros valores

Capítulo 3: Execução Condicional


Lógica de Programação com node.js

Exemplo Converter um número para uma string

Código ver exemplo 3.4

Capítulo 3: Execução Condicional


4. OPERADORES LÓGICOS
Na língua portuguesa há três palavras que comumente utilizamos: “não”, “e” e
“ou”.

A palavra “não” é utilizada para negar o sentido de frase ou expressões, por


exemplo:

 Não vai chover hoje;


 Não pretendemos comprar um carro;
 Não é hora de jantar.

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).

A palavra “ou” no português conecta frase ou expressões, muitas vezes dando um


sentido de exclusividade, por exemplo:

 Ou saímos ou ficamos em casa (apenas uma das expressões será


verdadeira);
 Devo estudar agora ou assistir ao filme? (escolha entre duas
possibilidades);
 Escrevo a resposta de lápis ou caneta? (escolha entre duas possibilidades).

Em linguagens de programação, essas três palavras também são muito


importantes, mas nem sempre têm o mesmo significado do português. Todas as três
funcionam como operadores que atuam sobre expressões booleanas, retornando
como resultado true ou false.

No caso do node.js, elas são representadas de acordo com tabela abaixo:

Tabela 4.1 - “não”, “e” e “ou” no node.js.


Português node.js Significado no node

Nega uma expressão:


não ! true vira false
false vira true

e && Resulta em true apenas quando todas as expressões são

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

verdadeiras

Resulta em false apenas quando todas as expressões são falsas


ou || (notem a diferença para o português, onde uma expressão teria
de ser falsa e a outra verdadeira)

4.1. Negação no node.js

No node.js, o operador da negação é representado pela exclamação: !. Esse


operador realiza uma operação relativamente simples, mas que também é muito
importante para as linguagens: transforma true em false, e false em true.

Uma das formas mais simples de compreender esse operador é através de uma
tabela com todas as suas possibilidades:

Tabela 4.2 - Tabela-verdade do operador da negação


Quando o valor de uma variável x é: O resultado da expressão !x é:

true false

false true

Vamos ver agora como a negação funciona na prática através de um exemplo de


código:

Exemplo 4.1 - Testes com o operador da negação.

1 var a = true

2 var b = false

3 var c = 10

4 var d = 5

5 var resultado = false

7 resultado = !a // resultado = false

8 resultado = !b // resultado = true

9 resultado = !(a == a) // resultado = false

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

10 resultado = !(a != a) // resultado = true

11 resultado = !(a != b) // resultado = false

12 resultado = !(c > d) // resultado = false

13 resultado = !(d < c) // resultado = false

4.1.1. Negação e Execução Condicional

Umas das principais aplicações da negação é o seu uso em conjunto com a


execução condicional.

Para ilustrar como ambas podem funcionar juntas, vamos implementar um


programa que testa se um número não é menor que zero:

Exemplo 4.2 - Testa se um número não é menor que zero.

1 var numero = 6 // o numero a ser testado

3 // imprime o numero a ser testado

4 console.log("numero a ser testado:", numero)

6 if (!(numero < 0)) { // testa se o numero nao é menor que zero

7 console.log("o numero passou no teste")

8 } else {

9 console.log("o numero nao passou no teste")

10 }

12

13 console.log("fim do programa")

Ao executarmos o programa acima, a saída do console seria:

numero a ser testado: 6


o numero passou no teste

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

fim do programa

Porém, quando a variável numero assume o valor -3, a saída do nosso exemplo no
console seria:

numero a ser testado: -3


o numero nao passou no teste
fim do programa

4.2. Conjunção no node.js

O operador “e lógico” é conhecido formalmente como conjunção lógica.

No node.js a conjunção lógica é representada pelo operador && (duplo “é


comercial”) e possui dois operandos (ou seja, atua sobre duas expressões distintas)
que devem ser booleanos.

O funcionamento do operador pode ser resumido da seguinte forma:

 Resulta em true somente quando ambas as expressões são verdadeiras;


 Resulta em false em todos os outros casos.

Assim, é possível explorar todas as possibilidades da conjunção através da sua


tabela-verdade:

Tabela 4.3 - Tabela-verdade do operador da conjunção.


Variável x igual a: Variável y igual a: Resultado de x && y:

true true true

true false false

false true false

false false false

Vamos ver agora como a conjunção funciona na prática através de um exemplo:

Exemplo 4.3 - Testes com o operador da conjunção.

1 var a = true

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

2 var b = false

3 var c = 10

4 var d = 5

5 var resultado = false

7 resultado = a && b // resultado = false

8 resultado = a && !b // resultado = true

9 resultado = (c > d) && (c > 0) // resultado = true

10 resultado = (c > d) && (c < 0) // resultado = false

11 resultado = (c == 0) && (d == 0) // resultado = false

12 resultado = (c != 0) && (d != 0) // resultado = true

13 resultado = (c % 2 == 0) && (d % 2 == 0) // resultado = false

14 resultado = (c % 2 == 0) && (d % 2 != 0) // resultado = true

4.2.1. Conjunção e Execução Condicional

Uma das principais aplicações da conjunção é o seu uso em conjunto com a


execução condicional.

Para ilustrar como ambas podem funcionar juntas, vamos implementar um


programa que testa se um número é par e maior que 10 ao mesmo tempo:

Exemplo 4.4 - Testa se um número é par ou maior que dez ao mesmo tempo.

1 var numero = 16 // o numero a ser testado

3 // imprime o numero a ser testado

4 console.log("numero a ser testado:", numero)

6 // testa se o numero é par e maior que 10 ao mesmo tempo

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

7 if ((numero % 2 == 0) && (numero > 10)) {

8 console.log("o numero passou no teste")

9 } else {

10 console.log("o numero nao passou no teste")

12 }

13

14 console.log("fim do programa")

Ao executarmos o programa acima, a saída do console seria:

numero a ser testado: 16


o numero passou no teste
fim do programa

Notem que o programa passa no teste porque o 16 é par e maior que 10.

Quando a variável numero assume o valor 6, a saída do nosso exemplo no console


seria:

numero a ser testado: 6


o numero nao passou no teste
fim do programa

Notem que o programa não passa no teste porque o 6 apesar de ser par, não é
maior que 10.

Quando a variável numero assume o valor 13, a saída do nosso exemplo no


console seria:

numero a ser testado: 13


o numero nao passou no teste
fim do programa

Notem que o programa não passa no teste porque o 13 apesar de ser maior que
10, não é par.

Quando a variável numero assume o valor 5, a saída do nosso exemplo no console


seria:

numero a ser testado: 5


o numero nao passou no teste
fim do programa

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

Notem que o programa não passa no teste porque o 5 não é maior que 10 e
também não é par.

4.3. Disjunção no node.js

O operador “ou lógico” é conhecido formalmente como disjunção lógica.

No node.js a disjunção lógica é representada pelo operador || (barras duplas) e


possui dois operandos (ou seja, atua sobre duas expressões distintas) que devem ser
booleanos.

O funcionamento do operador pode ser resumido da seguinte forma:

 Resulta em false somente quando ambas as expressões são falsas;


 Resulta em true em todos os outros casos.

Em resumo basta que qualquer dos operandos seja verdadeiro que a disjunção
entre eles também será verdadeira.

É importante observar que o operador da disjunção não funciona exatamente


como o “ou” na língua portuguesa. Em nossa língua, o “ou” representa uma expressão
onde exatamente (apenas) uma das opções é verdadeira e a outra é falsa. Em
linguagens de programação este comportamento é atribuído a outro operador,
conhecido como “ou exclusivo”, e que no node.js é representado pelo operador ^.

Agora que compreendemos como a disjunção funciona no node, é possível


explorar todas as suas possibilidades através da tabela-verdade abaixo:

Tabela 4.4 - Tabela-verdade do operador da disjunção.


Variável x igual a: Variável y igual a: Resultado de x && y:

true true true

true false true

false true true

false false false

Vamos ver agora como a disjunção funciona na prática através de um exemplo:

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

Exemplo 4.5 - Testes com o operador da disjunção.

1 var a = true

2 var b = false

3 var c = 10

4 var d = 5

5 var resultado = false

7 resultado = a || b // resultado = true

8 resultado = a || !b // resultado = true

9 resultado = (c > d) || (c > 0) // resultado = true

10 resultado = (c > d) || (c < 0) // resultado = true

11 resultado = (c == 0) || (d == 0) // resultado = false

12 resultado = (c != 0) || (d != 0) // resultado = true

13 resultado = (c % 2 == 0) || (d % 2 == 0) // resultado = true

14 resultado = (c % 2 != 0) || (d % 2 == 0) // resultado = false

4.3.1. Disjunção e execução Condicional

Uma das principais aplicações da disjunção é o seu uso em conjunto com a


execução condicional.

Para ilustrar como ambas podem funcionar juntas, vamos implementar um


programa que testa se um número é par ou maior que 10:

Exemplo 4.6 - Testa se um número é par ou maior que dez.

1 var numero = 16 // o numero a ser testado

3 // imprime o numero a ser testado

4 console.log("numero a ser testado:", numero)

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

6 // testa se o numero é par ou maior que 10

7 if ((numero % 2 == 0) || (numero > 10)) {

8 console.log("o numero passou no teste")

9 } else {

10 console.log("o numero nao passou no teste")

12 }

13

14 console.log("fim do programa")

Ao executarmos o programa acima, a saída do console seria:

numero a ser testado: 16


o numero passou no teste
fim do programa

Notem que o programa passa no teste porque o 16 é par e maior que zero.

Quando a variável numero assume o valor 6, a saída do nosso exemplo no console


seria:

numero a ser testado: 6


o numero passou no teste
fim do programa

Notem que o programa passa no teste porque o 6 é par, mesmo não sendo maior
que zero.

Quando a variável numero assume o valor 13, a saída do nosso exemplo no


console seria:

numero a ser testado: 13


o numero passou no teste
fim do programa

Notem que o programa passa no teste porque o 13 é maior que 10, mesmo não
sendo par.

Quando a variável numero assume o valor 5, a saída do nosso exemplo no console


seria:

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

numero a ser testado: 5


o numero nao passou no teste
fim do programa

Notem que o programa não passa no teste porque o 5 não é maior que 10, nem é
par.

4.4. Utilização da Negação, Conjunção e


Disjunção ao Mesmo Tempo

A utilização de vários operadores em conjunto é algo muito natural na


programação. E no caso dos operadores lógicos podemos fazer diversas
combinações entre a negação, a conjunção e a disjunção.

Como forma de exemplificar esse cenário, vamos trabalhar em um programa para


resolver o seguinte problema: dados dois números, identificar quando ambos os
números são pares ou maiores que 10:

Exemplo 4.7 - Dados dois números, identificar quando ambos os números são pares
ou maiores que 10.

1 var numero1 = 16 // o numero a ser testado

2 var numero2 = 18 // o numero a ser testado

4 // imprime o numero a ser testado

5 console.log("numeros a serem testados:", numero1, numero2)

7 // testa se o numero1 e numero2 são pares ou maiores que 10

8 if ((numero1 % 2 == 0) && (numero2 % 2 == 0)) || ((numero1 > 10)


&& (numero2 > 10))) { // notem o uso de parênteses na expressão

9 console.log("os numeros passaram no teste")

10 } else {

12 console.log("os numeros nao passaram no teste")

Capítulo 4: Operadores Lógicos


Lógica de Programação com node.js

13 }

14

15 console.log("fim do programa")

No exemplo 4.7, a linha 8 contém um conjunto expressões que combinam


conjunções e disjunções para garantir que o código do programa atenda à
especificação.

Quando precisamos combinar vários operadores em uma única expressão sempre


é importante utilizarmos os parênteses para garantir que nosso código funcione de
forma adequada. Na linha 8 do exemplo 4.7, elas são usadas para indicar que o
interpretador do código deve primeiro realizar as disjunções (&&) e só depois a
conjunção (||).

Capítulo 4: Operadores Lógicos


5. LAÇOS
Na língua portuguesa, a palavra "enquanto" tem um significado bem peculiar:
utilizamos o termo para representar a repetição de uma ou mais ações.

Por exemplo, notem o uso da palavra enquanto em um algoritmo para descrever


como passar ferro em um conjunto de camisas e armazená-las em um guarda-roupas:

Exemplo 5.1 - Algoritmo para passar ferro e guardar um conjunto de roupas

1 ligue o ferro de passar

2 aguarde 5 minutos para o ferro esquentar

3 enquanto há camisas

4 coloque a camisa na tábua

5 passe o ferro na camisa

6 coloque a camisa no cabide

7 coloque o cabide no guarda-roupas

8 desligue o ferro

9 feche o guarda-roupas

No exemplo acima, a palavra enquanto (linha 3) é utilizada para representar a


existência de um conjunto de ações que são executadas múltiplas vezes (linha 4 até a
linha 7). Essas linhas representam o processo de passar o ferro e guardar uma
camisa. Como há várias camisas a serem tratadas, essas linhas precisam ser
repetidas até que não haja mais camisas para passar. Ou seja, a repetição das linhas
ocorre até que uma determinada condição seja atendida. Por fim, quando a condição é
atendida, o programa encerra a sequência de repetições e segue normalmente para as
próximas linhas (linhas 8 e 9, no nosso exemplo).

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

Imagem 5.1 - Dinâmica de execução do algoritmo 1.1.

De forma geral, uma repetição em algoritmo funciona da seguinte forma:

Exemplo 5.2 - Modelo abstrato de uma repetição

1 enquanto condição

2 passo a ser repetido

3 passo a ser repetido

4 ...

5 fim da repetição

Em linguagens de programação como o node.js, essas repetições são chamadas


de laços. Há basicamente quatro tipos distintos de laços: while, for, do … while e
foreach. Os três primeiros serão abordados neste capítulo, enquanto que o quarto será
abordado no capítulo sobre arrays.

Capítulo 5: Laços
Lógica de Programação com node.js

5.1. while

No node.js a forma simples de implementarmos um laço é através de um bloco


while. A palavra while em inglês significa enquanto e sua construção segue um
modelo muito parecido com o que estudamos no início do capítulo:

Exemplo 5.3 - Modelo de um while

1 while (condição) {

2 passo a ser repetido

3 passo a ser repetido

4 ...

5 }

No modelo acima, todo o código que estiver entre as linhas 1 e 5 (intervalo


exclusivo) será repetido quando a condição for verdadeira. Toda vez que um ciclo da
repetição é feito, a condição é testada novamente. O laço só é encerrado quando a
condição torna-se falsa.

Diante disso, já podemos estudar um exemplo bem simples de laço: um


programa capaz de imprimir todos os números inteiros entre um intervalo inclusivo:

Exemplo 5.4 - Imprime todos os números inteiros entre um intervalo numérico.

1 var inicio = 5

2 var fim = 10

3 var numeroAtual = inicio

5 console.log("intervalo de", inicio, "ate", fim)

7 while (numeroAtual <= fim) { // enquanto ainda não chegamos no


fim

8 console.log(numeroAtual) // imprime o número atual

9 numeroAtual = numeroAtual + 1 // essa linha impede um laço

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.

Ao executarmos o programa com inicio = 5 e fim = 10, a seguinte saída será


gerada:

intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa

Porém, ao executarmos o programa com inicio = 10 e fim = 5, a seguinte saída será


gerada:

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

4 if (inicio <= fim) { // testa se o intervalo é válido

5 console.log("intervalo de", inicio, "ate", fim)

6 var numeroAtual = inicio

7 while (numeroAtual <= fim) {

8 console.log(numeroAtual)

9 numeroAtual = numeroAtual + 1

10 }

11 } else { // trata o caso do intervalo ser inválido

12 console.log("intervalo invalido")

13 }

14

15 console.log("fim do programa")

Ao executarmos o programa com inicio = 5 e fim = 10, a seguinte saída será


gerada:

intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa

Porém, ao executarmos o programa com inicio = 10 e fim = 5, a seguinte saída será


gerada:

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:

Exemplo 5.6 - Modelo de um for

1 for (declaração da variável; condição; atualização da variável)


{

2 passo a ser repetido

3 passo a ser repetido

4 ...

5 }

Para compreendermos como o for funciona corretamente é preciso entender cada


uma das três partes da sua composição. Assim, a declaração da variável é executada
apenas uma vez e é utilizada para declararmos a variável auxiliar que vai controlar a
execução do laço. Já a condição é executada a cada iteração do laço, assim como em
um while. Por fim, a atualização da variável é realizada a cada iteração do laço, porém
apenas após a execução das linhas que compõem o laço (linhas 2 a 4, no nosso
modelo).

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):

Exemplo 5.7 - Como um for funciona na prática.

1 declaração da variável // é como se a declaração fosse fora do


for

2 for (; condição;) {

3 passo a ser repetido

4 passo a ser repetido

5 ...

6 atualização da variável // a atualização é última linha


executada

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.

Agora que já entendemos como um for funciona, podemos passar para os


exemplos de código. Vamos explorar o mesmo problema da seção anterior: um
programa capaz de imprimir todos os números inteiros entre um intervalo inclusivo.
Isso irá nos permitir comparar melhor o for e o while.

Exemplo 5.8 - Imprime todos os números inteiros entre um intervalo numérico


usando um for.

1 var inicio = 5

2 var fim = 10

4 console.log("intervalo de", inicio, "ate", fim)

6 for (var numeroAtual = inicio; numeroAtual <= fim; numeroAtual++)


{

7 console.log(numeroAtual) // imprime o número atual

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.

Esse código é equivalente à sua versão com o while, e portanto, ao executarmos o


programa com inicio = 5 e fim = 10, a seguinte saída será gerada:

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

Mas também possui o mesmo problema com entradas inválidas, assim, ao


executarmos o programa com inicio = 10 e fim = 5, a seguinte saída será gerada:

intervalo de 10 ate 5
fim do programa

Dessa forma, vamos recorrer à mesma solução: utilizar um if:

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

4 if (inicio <= fim) { // testa se o intervalo é válido

5 console.log("intervalo de", inicio, "ate", fim)

7 for (var numeroAtual = inicio; numeroAtual <= fim;


numeroAtual++) {

8 console.log(numeroAtual) // imprime o número atual

9 }

10 } else { // trata o caso do intervalo ser inválido

11 console.log("intervalo invalido")

12 }

13

14 console.log("fim do programa")

Neste novo exemplo, ao executarmos o programa com inicio = 5 e fim = 10, a


seguinte saída será gerada:

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

Porém, ao executarmos o programa com inicio = 10 e fim = 5, a seguinte saída será


gerada:

intervalo de 10 ate 5
intervalo invalido
fim do programa

Ou seja, assim como no while, podemos combinar o for com um if.

5.3. do ... while

O do … while possui uma estrutura muito similar ao while. Contudo, o teste da


condição de parada é realizado apenas após a execução do código laço. Como
consequência, em todo o laço do tipo do … while é executado ao menos uma vez.

Os laços do tipo do … while possuem o seguinte modelo:

Exemplo 5.10 - Modelo de um do … while.

1 do {

2 passo a ser repetido

3 passo a ser repetido

4 ...

5 } while (condição) // a condição só é testa após a primeira


execução

Para compreendermos como o do … while funciona corretamente também


precisamos entender cada uma das três partes da sua composição. A palavra
reservada do neste contexto significa fazer. Assim, o início do laço indica que o
programa irá fazer (executar) o conjunto de linhas delimitadas pelas chaves { … }.
Assim, como nos demais laços, essas linhas podem conter basicamente qualquer uma
das construções que já estudamos como operações com variáveis, execuções
condicionais e até mesmo outros laços. Ao final do do … while encontramos a
condição de parada: while (condição), como a condição é escrita ao final do bloco de

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.

Agora que já compreendemos como um do … while funciona, podemos começar a


estudar exemplos de código. Vamos explorar o mesmo problema das seções
anteriores: um programa capaz de imprimir todos os números inteiros entre um
intervalo inclusivo.

Exemplo 5.11 - Imprime todos os números inteiros entre um intervalo numérico


usando um do … while.

1 var inicio = 5

2 var fim = 10

3 var numeroAtual = inicio

5 console.log("intervalo de", inicio, "ate", fim)

7 do {

8 console.log(numeroAtual) // imprime o número atual

9 numeroAtual = numeroAtual + 1

10 } while (numeroAtual <= fim)

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.

Inicialmente, o exemplo 5.11 pode parecer equivalente às versões escritas com o


while (exemplo 5.4) e com o for (exemplo 5.8). Como nas versões anteriores, ao
executarmos o programa com inicio = 5 e fim = 10, a seguinte saída será gerada:

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

Porém, ao executarmos o programa com inicio = 10 e fim = 5, a seguinte saída será


gerada:

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.

Exemplo 5.12 - Imprime todos os números inteiros entre um intervalo numérico


válido, utilizando um do … while.

1 var inicio = 5

2 var fim = 10

4 if (inicio <= fim) { // testa se o intervalo é válido

5 console.log("intervalo de", inicio, "ate", fim)

6 var numeroAtual = inicio

7 do {

8 console.log(numeroAtual)

9 numeroAtual = numeroAtual + 1

10 } while (numeroAtual <= fim)

11 } else { // trata o caso do intervalo ser inválido

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")

Neste exemplo mantemos o código funcionando corretamente ao executarmos o


programa com inicio = 5 e fim = 10, a seguinte saída será gerada:

intervalo de 5 ate 10
5
6
7
8
9
10
fim do programa

Porém, ao executarmos o programa com intervalo inicio = 10 e fim = 5, a seguinte


saída será gerada:

intervalo de 10 ate 5
intervalo invalido
fim do programa

Assim como no while e no for, podemos combinar o do … while com um if.

5.4. while, for ou do … while qual utilizar?

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:

Quadro 5.1 - Como utilizar as principais formas de execução condicional.


while

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

Exemplo Executar repetidamente um conjunto de linhas até que uma variável


booleana torne-se falsa.

Código while (condicao) {



condicao = false

}

for

Quando Quando há um contador ou uma variável (normalmente numérica) que


usar muda de valor a cada execução do laço

Exemplo Imprimir os 10 primeiros números inteiros

Código for (var i = 0; i < 10; i++) {


console.log(i)
}

do … while

Quando Quando é preciso executar o laço ao menos uma vez antes de testar a
usar condição de parada

Exemplo Imprimir o menu de opções no console

Código var opcao = 0

do {
console.log("Digite um 1 para jogar")
console.log("Digite um 2 para sair")
var opcao = lerOpcaoDoUsuario()
} while (opcao != 2)

5.5. Laços Aninhados

Ao longo deste capítulo estudamos como é possível utilizarmos laços em conjunto


com estruturas de execução condicional (exemplos 5.5, 5.9 e 5.12). A utilização de
laços em conjunto com outras estruturas de programação é algo muito comum.

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:

Exemplo 5.13 - imprime uma tabela @ (arrobas) de 10 linhas por 10 colunas.

1 var linhas = 10

2 var colunas = 10

4 console.log("tabela de ", linhas, "linhas por", colunas,


"colunas")

6 for (var i = 0; i < colunas; i++) { // imprime todas as linhas

7 for (var j = 0; j < colunas; j++) { // imprime uma linha


inteira

8 process.stdout.write("@") // imprime uma @ sem quebrar


a linha

9 } // fim do laço de dentro

10 console.log() // imprime uma linha em banco (pula uma


linha)

Capítulo 5: Laços
Lógica de Programação com node.js

11 } // fim do laço de fora

12

13 console.log("fim do programa")

A melhor forma de compreendermos os laços aninhados do exemplo 5.13 é


estudando o laço de dentro (linhas 7, 8 e 9) e depois o de fora (linhas 6 a 11).

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.

Ainda no exemplo 5.13 há outras duas novidades. Primeiramente, há uma


impressão através da função process.stdout.write na linha 8, essa linha imprime os
dados na tela de console sem realizar uma quebra de linha (sem pular uma linha entre
cada chamada). Além disso, também há uma impressão com a função console.log
sem parâmetros na linha 10, essa linha faz com que pulemos uma linha toda vez que
ela é executada.

Assim, se impressão na linha 8 fosse implementada com console.log cada arroba


seria impressa em uma linha própria (uma abaixo da outra). E sem a linha 10, todas as
arrobas seriam impressas na mesma linha (nossa tabela só teria uma linha com 100
arrobas).

No quadro abaixo há um exemplo que ilustra a diferença entre as funções


console.log e process.stdout.log:

Quadro 5.2 - Diferença entre as funções console.log e process.stout.log.


Trecho de código Saída no console

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")

5.6. Laços infinitos e como evitá-los

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.

Esse tipo de problema ocorre quando a condição de parada do laço nunca é


atendida. O exemplo 5.14 contém um código com um laço que nunca para e, por tanto,
gera um laço infinito:

Exemplo 5.14 - programa que gera um laço infinito usando um while (não o execute).

1 var condicao = true // condicao começa como true

3 while (condicao) { // o laço para quando a variável condicao é


falsa

4 console.log("laco em execucao") // condicao não muda dentro


do laço

5 }

7 console.log("fim do programa") // essa linha nunca é executada

O exemplo 5.14 começa declarando a variável condicao que é inicializada com o


valor true. Em seguida, a linha 3 inicia um laço que irá parar assim que a variável
condicao torna-se false. O laço possui em seu interior apenas uma linha (a linha 4) que
imprime no console o texto “laco em execucao”. Não há nada no interior do laço que
altere a variável condicao para o valor false. Consequentemente, o laço nunca para e o
programa fica preso entre as linhas 3, 4 e 5. Note também como a linha 7 nunca será
executada.

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:

Exemplo 5.15 - evitando o laço infinito do exemplo 5.14.

1 var condicao = true

3 while (condicao) { // o laço para quando a variável condicao é


falsa

4 console.log("laco em execucao")

5 condicao = false // altera condicao evitando um laço


infinito

6 }

8 console.log("fim do programa") // essa linha nunca é executada

Quando utilizamos um for, é muito comum que a variável que controla o laço
nunca seja alterada ou seja alterada incorretamente.

O exemplo 5.16 demonstra um laço infinito onde o contador (variável i) é


decrementado ao invés de incrementado gerando assim um laço infinito:

Exemplo 5.16 - programa que gera um laço infinito usando um for (não o execute).

1 var condicao = true

3 for (var i = 0; i < 10; i = i - 1) { // o laço para quando i


igual a 10

4 console.log("laco em execucao")

Capítulo 5: Laços
Lógica de Programação com node.js

5 }

7 console.log("fim do programa") // essa linha nunca é executada

Contudo, uma simples correção na atualização do contador será capaz de evitar o


laço infinito:

Exemplo 5.17 - evitando o laço infinito do exemplo 5.16.

1 var condicao = true

2 // note como a alteração do contato agora soma ou invés de


subtrair

3 for (var i = 0; i < 10; i = i + 1) { // o laço para quando i


igual a 10

4 console.log("laco em execucao")

5 }

7 console.log("fim do programa") // essa linha nunca é executada

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.

A reusabilidade representa o quão fácil o código pode ser reutilizado em outras


partes do programa, de forma geral, tentamos evitar trechos de código duplicados no
projeto para que não tenhamos que resolver o mesmo problema mais de uma vez.

A legibilidade é a capacidade que um outro programador tem de compreender o


código ao lê-lo, ou seja, o quão fácil é para outros programadores entender o que o
código faz.

Por fim, a manutenibilidade mede o quão fácil é de se alterar o código. Como


alterações no código são comuns, seja por conta de correções de bugs, seja pela
adição de novas funcionalidades, sempre buscamos escrever um código de fácil
manutenção.

De forma geral, um código bem escrito deve buscar ter essas três características.

Para ilustrar como esses conceitos funcionam na prática, vamos desenvolver um


código capaz de analisar se um mês é válido ou não, ou seja, se está no intervalo
inclusivo entre 1 e 12:

Exemplo 6.1 - Programa para validação do mês2.

1 var mes = 5

3 // === testa se dois valores são iguais e do mesmo tipo

4 if ((mes % 1 === 0) && (mes >= 1) && (mes <= 12)) {

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

A solução acima possui importantes problemas nas três medidas de qualidade de


código que estamos discutindo. Ela não é de fácil reuso porque toda vez que
tentarmos reusá-la teríamos que copiar 8 linhas de código. Isso também prejudicaria a
manutenção do código porque ele estaria duplicado em várias partes do programa,
como faríamos para encontrar todas as replicações dele, caso alguém encontre um
bug? Por fim, ela não é de fácil leitura porque é longo e usa uns operadores pouco
convencionais (=== e %).

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.

Um subprograma é um trecho de código criado para representar a solução para um


problema específico e que pode ser facilmente reusável.

Utilizando subprogramas, necessitamos apenas de uma linha de código para


reutilizar várias outras linhas.

Além disso, podemos dar nomes a um subprograma de forma que ele tenha sua
funcionalidade facilmente compreendida.

Abaixo há um modelo abstrato do código de um subprograma:

Exemplo 6.2 - Modelo abstrato de um subprograma.

1 // a palavra reservada function indica o início de uma função

2 function nome(lista de parâmetros) { // parâmetros são como


variáveis

3 código // variáveis, ifs, laços, etc.

4 }

6.1. Subprogramas Procedimentais

Subprogramas procedimentais são a forma mais simples de subprogramas. Eles


apenas executam um conjunto de linhas de código isoladas dentro de uma função.

Por exemplo, o subprograma abaixo é capaz de multiplicar dois números e


imprimir o resultado da multiplicação:

Capítulo 6: Subprogramas
Lógica de Programação com node.js

Exemplo 6.3 - Multiplica dois números e imprime o resultado da multiplicação.

1 // função de nome multiplicar que recebe dois parâmetros: a e b

2 function multiplicar(a, b) {

3 var resultado = a * b

4 console.log("valor da multiplicacao:", resultado)

5 } // nesta linha o código volta para a linha onde a função foi


chamada

7 // para chamarmos uma função usamos o seu nome e passamos os


parâmetros

8 multiplicar(20, 40) // nesta linha o código salta para linha 2

9 multiplicar(3, -2) // nesta linha o código salta para linha 2


novamente

10

11 var x = 5

12 var y = 6

13

14 multiplicar(x, y) // parâmetros também podem ser variáveis

Qual seria a saída no console para o programa do exemplo 6.3?

valor da multiplicacao: 800


valor da multiplicacao: -6
valor da multiplicacao: 30

Para compreendermos melhor como a saída do console contém apenas três


linhas, vamos explorar a ordem de execução das linhas ao rodarmos o exemplo 6.3:

Quadro 6.1 - Ordem de execução das linhas do exemplo anterior.

Ordem Linha Código da linha em execução Comentário

1 8 multiplicar(20, 40) O programa começa na linha


8

2 2 function multiplicar(a, b) { Salto para a linha 2

Capítulo 6: Subprogramas
Lógica de Programação com node.js

3 3 var resultado = a * b Execução da função

4 4 console.log("valor da Execução da função


...

5 5 } Fim da função

6 8 multiplicar(20, 40) a linha que chamou a


função

7 9 multiplicar(3, -2) Nova chamada à função

8 2 function multiplicar(a, b) { Salto para a linha 2

9 3 var resultado = a * b Execução da função

10 4 console.log("valor da Execução da função


...

11 5 } Fim da função

12 9 multiplicar(3, -2) Linha que chamou a função

13 11 var x = 5

14 12 var y = 6

15 14 multiplicar(x, y) Nova chamada à função

16 2 function multiplicar(a, b) { Salto para a linha 2

17 3 var resultado = a * b Execução da função

18 4 console.log("valor da Execução da função


...

19 5 } Fim da função

20 14 multiplicar(x, y) Linha que chamou a função

Notas importantes sobre funções:

 Os parâmetros da função podem ser constantes literais (linhas 8 e 9 do


exemplo 6.3), constantes ou variáveis (linha 14 do exemplo 6.3);
 Toda chamada de função gera um salto no código para a primeira linha da
função;
 Na linha 14, quando chamamos multiplicar(x, y), a assume o mesmo valor
(uma cópia) de x e b assume o mesmo valor de y (também uma cópia)
o Ou seja, se alterarmos a ou b dentro da função estamos alterando
uma cópia dos parâmetros, mas não dos parâmetros atuais,
chamamos esse tipo de passagem de parâmetros de passagem por
valor;

Capítulo 6: Subprogramas
Lógica de Programação com node.js

o Alguns tipos de parâmetros como arrays e objetos de forma geral


são passados como referências, mas isto é assunto para outro
capítulo.

6.2. Subprogramas Funcionais

Subprogramas funcionais atuam como funções matemáticas. Assim, eles


computam um valor e o retornam como resultado, a linha de código que chamou a
função é responsável por tratar o valor retornado.

O retorno de um subprograma é feito através da palavra reservada return.

Um subprograma pode conter quantos return’s forem necessários, porém o


excesso de return’s pode prejudicar a legibilidade do código

Podemos reescrever o subprograma capaz de multiplicar dois números de maneira


funcional:

Exemplo 6.4 - Multiplica dois números e retorna o resultado da multiplicação.

1 // função de nome multiplicar que recebe dois parâmetros: a e b

2 function multiplicar(a, b) {

3 return a * b // encerra a função retornando o valor da


multiplicação

5 }

7 // guarda o valor retornado em resultado

8 var resultado = multiplicar(20, 40)

9 console.log("valor da multiplicacao:", resultado) // imprime


resultado

10 // também é possível chamar a função dentro do console.log

11 console.log("valor da multiplicacao:", multiplicar(3, -2))

12

13 var x = 5

Capítulo 6: Subprogramas
Lógica de Programação com node.js

14 var y = 6

15 // também é possível chamar a função tendo variáveis como


parâmetros

16 console.log("valor da multiplicacao:", multiplicar(x, y))

Qual seria a saída no console para o programa do exemplo 6.4?

valor da multiplicacao: 800


valor da multiplicacao: -6
valor da multiplicacao: 30

Na prática, o resultado do exemplo acima é o mesmo do exemplo anterior.

Agora vamos voltar agora ao problema do início do capítulo: validação do valor


numérico de um mês:

Exemplo 6.5 - Programa para validação do mês.

1 // retorna true se o mês for válido ou false caso contrário

2 function validarMes(mes) {

3 return mes % 1 === 0 && mes >= 1 && (mes <= 12)

4 }

6 console.log("data valida: ", validarMes(5)) // testa a função

Por que o exemplo acima é melhor do que o anterior?

Para testarmos se um mês é válido só precisamos chamar a função validarMes,


que abstrai todos os detalhes complicados de como o teste é feito, assim melhoramos
a legibilidade do código.

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

6.3. Quando utilizar subprogramas funcionais


e quando subprogramas procedimentais?

Em muitos casos podemos utilizar tanto subprogramas funcionais quanto


procedimentais para implementar nossos programas. Entretanto, na maioria dos
casos optamos por subprogramas funcionais porque eles não contêm código de
interface (como chamadas à função consoles.log) e porque o retorno de informações
após a chamada muitas vezes é necessário.

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:

 Computação de equações e resultados de funções matemáticas;


 Buscas em coleções de dados;
 Requisições de dados à servidores da internet;
 Validação de dados como entrada aos nossos programas pelos seus
usuários;

Enquanto isso, subprogramas procedimentais devem ser usados quando não há


motivos para se retornar explicitamente dados ao trecho de código que chamou o
subprograma. Os seguintes exemplos de problemas podem ser naturalmente
resolvidos através de subprogramas funcionais:

 Funções de depuração (imprimem o valor de variáveis no console para


encontrarmos onde estão os bugs do nosso código);
 Código de interface gráfica no console;
 Funções com efeito colateral e/ou passagem por referência sobre arrays e
tipos compostos (que alteram seus parâmetros ou variáveis globais), esses
conceitos são explorados nos capítulos futuros do livro;
 Código orientado a objetos que diferente do paradigma imperativo, usam
subprogramas (conhecidos como métodos neste paradigma) para alterar
os dados armazenados na memória (objetos) de forma que quase sempre
não é necessário retornarmos um resultado.

6.4. Escopo, variáveis globais e variáveis


locais

Capítulo 6: Subprogramas
Lógica de Programação com node.js

Em linguagens de programação imperativas há dois tipos básicos de variáveis: as


variáveis globais (que são visíveis e podem ser usadas em todo o programa ou
arquivo) e as variáveis locais (que são visíveis e podem ser usadas apenas em regiões
específicas do código).

A região do código onde uma variável é visível é conhecida como escopo da


variável, qualquer utilização da variável fora do seu escopo pode resultar em um valor
undefined, um erro de execução, ou simplesmente na criação de uma variável nova e
independente da variável original.

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:

Exemplo 6.6 - Variáveis globais.

1 var a = 5 // declarando a variável global a

2 let b = 10 // declarando a variável global b

3 c = 2 // declarando a variável global c

6 function multiplicar() { // variáveis globais são visíveis em


funções

7 return a * b * c // funciona porque a, b e c são globais

8 }

10 console.log("valor de a", a) // a é global

11 console.log("valor de b", b) // b é global

12 console.log("valor de c", c) // c é global

13

14 var resultado = multiplicar()

15 console.log("valor da multiplicacao:", resultado)

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:

Exemplo 6.7 - Variáveis locais.

1 function multiplicar() { // a, b e c são variáveis locais à


função

2 var a = 5 // declarando a variável local a

3 let b = 10 // declarando a variável local b

5 c = 2 // declarando a variável local c

7 return a * b * c // funciona porque a, b e c são locai à


função

8 }

10 // ao tentarmos usar uma variável fora do seu escopo o programa


dá um erro e simplesmente para de ser executado pelo
interpretador

11 console.log("valor de a", a) // erro: a não foi definida

12 console.log("valor de b", b) // erro: b não foi definida

13 console.log("valor de c", c) // erro: c não foi definida

14

15 var resultado = multiplicar()

16 console.log("valor da multiplicacao:", resultado)

Quando lidamos com escopo de variáveis a utilização do let, do var ou a simples


não utilização deles geram resultados diferentes:

Exemplo 6.8 - Diferença de escopo entre as distintas formas de declaração.

1 function multiplicar() { // a, b e c são variáveis locais à


função

2 var a = 5 // declarando a variável local a

3 let b = 10 // declarando a variável local b

Capítulo 6: Subprogramas
Lógica de Programação com node.js

5 c = 2 // c torna-se global quando a função é executada

7 return a * b * c // funciona porque a, b e c são locai à


função

8 }

10 // quando a função é executada a, b e c são criadas

11 var resultado = multiplicar()

12 console.log("valor da multiplicacao:", resultado)

13

14 console.log("valor de a", a) // erro: a não foi definida

15 console.log("valor de b", b) // erro: b não foi definida

16 console.log("valor de c", c) // funciona pois c é uma variável


global

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.

Também há diferenças importantes no momento que uma variável declarada com


var, let ou sem nenhum modificador torna-se visível:

Exemplo 6.9 - Mostra quando variáveis tornam-se visíveis no código.

1 // imprime undefined, o escopo de a começa antes da própria


declaração:

2 console.log("valor de a", a) // não gera erro, o programa


continua

3 console.log("valor de b", b) // erro: b não existe, para o


programa

5 console.log("valor de c", c) // erro: c não existe, para o


programa

Capítulo 6: Subprogramas
Lógica de Programação com node.js

7 var a = 5 // declarando a variável global a

8 let b = 10 // declarando a variável global b

9 c = 2 // declarando a variável global c

10

11 console.log("valor de a", a) // funciona pois a é visível neste


escopo

12 console.log("valor de b", b) // funciona pois b é visível neste


escopo

13 console.log("valor de c", c) // funciona pois c é visível neste


escopo

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.

No caso do exemplo 6.9, a impressão do undefined na linha 2 significa que a


variável existe, mas não teve ainda um valor atribuído a ela. Porém, nos demais casos
(linhas 2 e 3), ocorre que as variáveis b e c não existem ainda e por tanto o
interpretador para de executar o programa gerando assim um erro de execução.

Um resumo de como funciona cada modificador e quando eles devem ser


utilizados pode ser encontrado no quadro abaixo:

Quadro 6.2 - Escopo das variáveis com var, let e sem modificador.
var

Como A variável torna-se localmente visível no início do escopo na qual foi


funciona? declarada, mas a linha que a inicializa precisa ser executada para não
resultar em um valor undefined.

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.

Exemplo de console.log(a) // a é visível, imprimiria undefined


código var a = 15
console.log(a) // a é visível, imprimiria 15

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.

Quando O let é o modificador recomendado para a vasta maioria dos casos


usar? porque quando utilizamos uma variável que ainda não exista ele vai
sempre parar a execução do programa evitando comportamentos não
planejados pelo programador.

Exemplo de console.log(a) // a não é visível, pararia a execução do programa


código let a = 15
console.log(a) // a é visível, imprimiria 15

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.

Quando Quando precisamos declarar uma variável global dentro de um escopo


usar? de uma função. De forma geral, não é uma boa ideia declarar variáveis
sem o modificador porque como elas são globais poderemos ter
conflitos com variáveis do mesmo nome declaradas em escopos locais.

Exemplo de function xyz() {


código a = 15 // quanto esta linha é executada a torna-se uma variável global
}

xyz() // quanto esta função é executada a torna-se uma variável global


console.log(a) // a é visível, imprimiria 15

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:

Exemplo 6.10 - Conflitos de escopo.

1 var a = 5 // declarando a variável global a

3 function multiplicar() { // a, b e c são variáveis locais à


função

5 var a = 7 // notem que já há uma variável (global) com o


mesmo nome

6 let b = 10

7 // usa o a e o b locais

Capítulo 6: Subprogramas
Lógica de Programação com node.js

8 return a * b // retorna 70 porque o escopo local tem


prioridade

9 }

10

11 console.log("valor de a", a) // imprime 5 porque usa o escopo


global

12

13 var resultado = multiplicar()

14 console.log("valor da multiplicacao:", resultado) // imprime 70

Por conta dos conflitos de escopo e também por conta da geração de


dependências globais desnecessárias dentro de subprogramas, quando possível,
tentamos evitar o uso de variáveis globais.

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.

Assim, strings representam um exemplo de como as coleções de dados são úteis


em determinados contextos. De forma geral coleções de dados são úteis em
problemas como: armazenar as notas dos estudantes de Introdução à Programação;
computar a média geral da turma no componente curricular; criar um estoque de
mercadorias para uma loja; etc. Neste capítulo vamos generalizar como linguagens de
programação podem armazenar coleções de dados na memória através do conceito
de vetores.

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.

7.1. Declaração e Inicialização de Vetores

Os primeiros passos para trabalharmos com vetores dentro de qualquer linguagem


de programação são a declaração e inicialização:

Exemplo 7.1 - Declaração de Inicialização de Vetores

1 var vetorVazio1 = new Array() // declara um vetor vazio

2 var vetorVazio2 = [] // outra forma de declarar um vetor vazio

4 var vetorDeNumeros1 = new Array(3, 60, 0) // vetor de três


elementos

5 var vetorDeNumeros2 = [3, 60, 0] // vetor de três elementos

7 var vetorDeStrings1 = new Array("3", "60", "a") // três strings

8 var vetorDeStrings2 = ["3", "60", "a"] // três strings

10 var vetorHibrido1 = new Array(3, "60", false) // tipos


distintos

11 var vetorHibrido2 = [3, "60", false] // tipos distintos

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.

Há duas sintaxes distintas para inicialização de vetores:

Capítulo 7: Vetores
Lógica de Programação com node.js

 Inicialização através do construtor3 Array, utilizando a palavra reservada


new e passando para o construtor uma lista de elementos (linhas 1, 4, 7 e
10 do exemplo 7.1);
 Utilizando colchetes (como na matemática) para determinar os elementos
do vetor (linhas 2, 5, 8 e 11 do exemplo 7.1).

Cada elemento do vetor possui um índice próprio, por exemplo ao declararmos:

var vetorHibrido2 = [3, "60", false]

Os índices estão organizados da seguinte forma: 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.

Há duas sintaxes distintas para inicialização de vetores:

 Inicialização através do construtor Array, utilizando a palavra reservada


new e passando para o construtor uma lista de elementos (linhas 1, 4, 7
e 10 do exemplo 7.1);
 Utilizando colchetes (como na matemática) para determinar os
elementos do vetor (linhas 2, 5, 8 e 11 do exemplo 7.1).

Cada elemento do vetor possui um índice próprio, por exemplo ao declararmos:

var vetorHibrido2 = [3, "60", false]

Os índices estão organizados da seguinte forma:

Índice Valor

0 3

1 “60”

2 false
Tabela 7.2 - Índices e valores da varíavel vetorHibrido2.

Ou seja, no node.js os índices de um vetor vão do 0 (zero) até o tamanho - 1 do


vetor.

3
Mais sobre construtores no capítulo sobre tipos compostos.

Capítulo 7: Vetores
Lógica de Programação com node.js

7.2. Acessando e Editando Vetores

Para termos acesso aos valores dos elementos presentes em um vetor fazemos
uso de seus índices:

Exemplo 7.2 - Acesso aos elementos de um vetor.

1 var vetorHibrido = [3, "60", false]

3 var valor = vetorHibrido[0] // valor = 3 porque 3 está no


índice 0

4 valor = vetorHibrido[1] // valor = "60" porque "60" está no


índice 1

5 valor = vetorHibrido[2] // valor = false porque false está no


índice 2

Ao utilizarmos os índices temos acesso aos valores de um vetor, também é


importante observar que devemos utilizar índices válidos na hora do acesso. No caso
do exemplo 7.2 apenas os índices 0, 1 e 2 são válidos.

O que ocorreria se tentássemos utilizar um índice inválido dentro de um vetor?

Por exemplo:

valor = vetorHibrido[10]

No node.js, como não há nada no índice 10 do vetor, a variável valor conteria um


dado undefined.

Para editarmos (alterarmos) os valores de um vetor também fazemos uso dos


seus índices:

Exemplo 7.3 - Edição dos elementos de um vetor.

1 var vetorHibrido = [3, "60", false]

3 vetorHibrido[0] = true // o valor do índice 0 passa a ser true

Capítulo 7: Vetores
Lógica de Programação com node.js

4 vetorHibrido[1] = -1 // o valor do índice 1 passa a ser -1

5 vetorHibrido[2] = "bola" // o valor do índice 2 passa a ser "bola"

É importante observar com calma os índices utilizados na hora do acesso. No caso


do exemplo 7.3, apenas os índices 0, 1 e 2 estão presentes no vetor.

O que se aconteceria se tentássemos utilizar um índice que ainda não existe no


vetor?

Por exemplo, ao adicionarmos uma linha extra ao código do exemplo 7.3:

vetorHibrido[4] = 7

Como não há nada no índice 4 do vetor, o interpretador do node.js cria e inicializa


novos índices no vetor para que ele tenha o tamanho necessário para a linha
funcionar.

Assim, o valor da variável vetorHibrido ao final da execução da linha


vetorHibrido[4] = 7 seria:

[true, -1, "bola", null, 7]

7.3. Tamanho de um Vetor

Uma das propriedades mais importantes de um vetor é o seu tamanho.

Todas as linguagens de programação possuem uma forma de termos acesso ao


tamanho de um vetor e no node.js isso não é diferente.

No node.js, podemos descobrir o tamanho de um vetor através da propriedade


length:

Exemplo 7.4 - Tamanho de um vetor.

1 var nomes = ["bola", "carro", "escola"]

3 var tamanho = nomes.length // tamanho = 3

Capítulo 7: Vetores
Lógica de Programação com node.js

7.4. Vetores e Laços

Fazendo uso do tamanho de um vetor e do conceito de laços podemos


desenvolver soluções relativamente simples para um conjunto de problemas que até
então não éramos capazes.

Por exemplo, o problema de computar a média dos estudantes de uma


determinada matéria:

Exemplo 7.5 - Computa a média dos estudantes.

1 var notas = [3.6, 4.5, 2, 9, 7.5] // as notas dos estudantes

2 var media = 0 // guardará o valor da média

4 for (var i = 0; i < notas.length; i++) {

5 media = media + notas[i] // soma das notas, elemento a


elemento

6 }

8 media = media / notas.length // a soma das notas torna-se a


média após a divisão

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).

Na linha 4 podemos observar que a variável i é inicializada com 0 e vai sendo


incrementada de um em um. Além disso, a condição de parada do laço é quando i
chega em um valor que não é mais inferior ao tamanho do vetor.

Assim, ao final do laço todos os elementos do vetor foram visitados e a variável


media passa a guardar a soma dos valores contidos no vetor.

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.

No exemplo 7.5, utilizamos um for para visitar os elementos de um vetor.


Entretanto, também poderíamos utilizar um while ou um do ... while.

A ordem de acesso aos índices do vetor media representa no exemplo 7.5 é


apenas uma dentre as possíveis.

Há diversas formas de se visitar um vetor, conforme ilustrado na tabela abaixo:

Ordem de acesso Valor inicial do contador Condição de parada Atualização

normal let i = 0 i < vetor.length i++

inversa let i = vetor.length - 1 i > -1 i--

índices pares let i = 0 i < vetor.length i = i + 2

índices ímpares let i = 1 i < vetor.length i = i + 2

Tabela 7.3 - Formas básicas de se vistar os elementos de um vetor

Outra forma importante de se visitar os elementos de um vetor é através de um


tipo de laço especial conhecido como for ... in4.

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.

No do node.js o foreach é implementado utilizando a palavra reservada in:

Exemplo 7.6 - Computa a média dos estudantes com um laço foreach.

1 let notas = [3.6, 4.5, 2, 9, 7.5] // as notas dos estudantes

2 let media = 0 // guardará o valor da média

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

4 // para cada elemento no vetor notas

5 for (indice in notas) { // notem como não há contador nem


condição

6 media = media + notas[indice] // indice muda a cada


iteração

7 }

9 media = media / notas.length // a soma das notas torna-se a


média após a divisão

10

11 console.log(media)

O exemplo 7.6 é equivalente ao exemplo 7.5, na prática, ao utilizarmos um foreach


o interpretador do node.js cria o contador e testa a condição de parada durante a
execução do código.

Enquanto em um for tradicional temos total controle da ordem e de quais


elementos serão visitados, no foreach só podemos visitar todos5 os elementos e na
ordem normal de seus índices (do 0 ao tamanho - 1).

Assim, apesar de simplificar o código, o foreach tem uma aplicação bem


específica e limitada.

7.5. Vetores e Subprogramas

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.

O uso de vetores em subprogramas nos ajuda a isolar trechos do nosso código


que se estivessem soltos no meio do código poderiam ser difíceis de compreender,

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

mas separados em um subprograma ajudam bastante na legibilidade do nosso


código.

O exemplo abaixo demonstra como poderíamos utilizar um subprograma para


resolver o problema de computar a média das notas dos estudantes:

Exemplo 7.7 - Computa a média dos estudantes com um laço foreach em um


subprograma

1 function media(notas) { // notem como notas agora é um parâmetro

2 let media = 0 // guardará o valor da média

4 // para cada elemento no vetor notas

5 for (indice in notas) { // notem como não há contador nem


condição

6 media = media + notas[indice] // indice muda a cada


iteração

7 }

8 // a soma das notas torna-se a média após a divisão

9 media = media / notas.length

10

11 return media

12 }

13

14 let notas = [3.6, 4.5, 2, 9, 7.5]

15

16 resultado = media(notas) // passando o vetor como parâmetro

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.

7.5.1. Passagem por valor e passagem por


referência

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:

Exemplo 7.8 - Passagem de parâmetro por valor.

1 function alterar(numero) { // notem como numero é uma cópia da


variável

2 numero = 10 // numero passa a ser 10, mas valor não é


alterado

3 }

5 let valor = 15 // valor começa com 15

7 console.log(valor) // imprime 15

9 alterar(valor) // passa uma cópia de valor como parâmetro para


a função

10

11 console.log(valor) // imprime 15 novamente

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.

O exemplo de comportamento de função presente no exemplo 7.8 mostra aquilo


que chamamos de passagem de parâmetro por cópia.

Capítulo 7: Vetores
Lógica de Programação com node.js

Porém, em alguns casos também é possível passarmos referências para variáveis


como parâmetro. Nesse contexto, o comportamento é o oposto: ao alterar o
parâmetro da função, a variável original também é alterada. Esse tipo de
comportamento é conhecido como passagem de parâmetro por referência.

No node.js, a passagem por referência ocorre quando passamos como parâmetro


objetos (tipos não primitivos) para nossas funções e no node.js vetores são objetos.
Estudaremos o conceito de objetos no capítulo sobre registros.

Assim ao criarmos uma função que recebe um vetor como parâmetro podemos
observar como a passagem por referência se comporta:

Exemplo 7.9 - Passagem de parâmetro por referência.

1 function alterar(parametro) { // parametro é uma referência

2 // altera a variável parametro e o vetor original

3 parametro[1] = 10

4 }

5 let vetor = [3.6, 4.5, 2, 9, 7.5] // vetor original

7 console.log(vetor) // imprime [3.6, 4.5, 2, 9, 7.5]

9 alterar(vetor) // passa uma referência de vetor para a função

10

11 // notem como o índice 1 do vetor é alterado

12 console.log(vetor) // imprime [3.6, 10, 2, 9, 7.5]

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.

7.6. Vetores e Strings

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:

Exemplo 7.10 - Strings como vetores

1 let string = "bola" // declara uma string contendo a palavra bola

3 // acessando um caractere de uma string pelo seu índice

4 console.log(string[2]) // imprime l

5 string[2] = "t" // strings são imutáveis, por isso não podemos


editá-las

6 console.log(string[2]) // ainda imprime l

8 // também podemos usar laços para percorrer strings

9 for (let i = 0; i < string.length; i++) {

10 console.log(string[i]) // imprime o caractere de índice i

11 }

No exemplo 7.10 podemos observar como as formas de manipulação de um vetor


também podem ser usadas para manipular strings. Contudo, entre as linhas 4 e 6 é
feito um acesso (linha 4), uma tentativa de alteração da string (linha 5) que não
funciona porque strings são imutáveis no node.js e um novo acesso (linha 6). As linhas
4 e 6 demonstram como usar os índices para acessar os caracteres de uma string. Já
entre as linhas 9 e 11 a string é visitada caractere por caractere usando um laço assim
como fazermos com um vetor.

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.

7.6.1. Funções úteis para Strings

Capítulo 7: Vetores
Lógica de Programação com node.js

Como na maioria das linguagens de programação, o node.js possui um conjunto


de funções (no caso do node.js, o termo mais correto seria métodos) que podem ser
utilizadas para facilitar a manipulação de strings. De forma geral, essas funções são
capazes criar e retornar novas strings usando as strings originais como cópia:

Quadro 7.1 - Funções úteis para o tratamento de strings.


Função (método) Comportamento Exemplo de uso

Cria e retorna uma let s = "carro bola boneca"


substring da original que
slice(start, end)
começa no índice start e // g= "bola"
termina no índice end. let g = s.slice(6, 10)

let s = "carro bola boneca"


Idêntica a slice, porém
substring(start, end) índices menores que 0 são
// g = "carro bola"
tratados como zero
let g = s.substring(-1, 10)

Similar a slice substring, let s = "carro bola boneca"


porém o segundo
substr(start, length)
parâmetro indica o tamanho // g = "bola"
da substring. let g = s.substring(6, 4)

Cria e retorna uma nova


string onde a primeira
ocorrência de currentValue
let s = "carro boneca carro"
é trocada por newValue.
replace(currentValue,
// g = "moto boneta moto"
newValue) Para trocar as demais
let g = s.replace("carro",
ocorrências é necessário
"moto")
que currentValue seja no
formato de uma expressão
regular6.

Cria e retorna uma nova


let s = "bola"
string onde todos os
toUpperCase() caracteres da string original
// g = "BOLA"
são transformados em
let g = s.toUpperCase()
letras maiúsculas.

Cria e retorna uma nova


let s = "BOLA"
string onde todos os
toLowerCase caracteres da string original
// g = "bola"
são transformados em
let g = s.toUpperCase()
letras minúsculas.

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)

Cria e retorna uma nova


let s = " carro boneca "
string com os caracteres
trim vazios no começo e no fim
// g = "carro boneca"
da string original
let g = s.trim()
removidos.

Cria e retorna uma nova let s = " carro boneca "


string com os caracteres
trimStart
vazios no começo da string // g = "carro boneca "
original removidos. let g = s.trim()

Cria e retorna uma nova let s = " carro boneca "


string com os caracteres
trimEnd
vazios no fim da string // g = " carro boneca"
original removidos. let g = s.trim()

O portal da W3 Schools possui uma excelente documentação sobre essas e outras


funções para manipulação de strings:
https://www.w3schools.com/js/js_string_methods.asp.

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.

Esse modelo de estrutura é muito utilizado em soluções de problemas onde


precisamos de uma coleção de dados como armazenar as notas dos estudantes em
uma determinada matéria.

Porém, em casos mais complexos, um simples vetor não é capaz de armazenar de


forma adequada todas as informações que precisamos.

Por exemplo, como poderíamos armazenar todas as informações dos estudantes


em um determinado semestre? Utilizando vetores, o que poderíamos fazer seria um
vetor diferente para armazenar as notas de cada matéria ou de cada estudante.

Na verdade, uma forma mais eficiente de armazenar essas informações seria


através de uma tabela:

Estudante Lógica de Prog. Redes Inglês Design Cálculo

João 7.0 6.0 7.5 8.0 6.5

Maria 9.5 5.75 8.5 7.5 9.0

Carlos 4.5 8.0 7.5 6.5 7.0

Valéria 6.0 5.0 6.5 7.0 8.5


Tabela 8.1 - Armazenamento das notas dos estudantes em uma tabela.

Em programas de computadores, tabelas com o exemplo acima são representadas


através de arrays de duas dimensões.

Arrays são a generalização do conceito de vetores. Um vetor possui apenas uma


dimensão, já um array pode ter n dimensões. Na prática, todo vetor também é um array
de uma dimensão.

Assim, cada elemento dentro de um array possui dois índices, um representando


sua linha e outro representando sua coluna:

linha\coluna 0 1 2 3 4

0 (0,0) (0,1) (0,2) (0,3) (0,4)

1 (1,0) (1,1) (1,2) (1,3) (1,4)

Capítulo 8: Arrays
Lógica de Programação com node.js

2 (2,0) (2,1) (2,2) (2,3) (2,4)

3 (3,0) (3,1) (3,2) (3,3) (3,4)


Tabela 8.2 - Indexação dentro de um array bidimensional.

As mesmas propriedades básicas de um vetor também podem ser aplicadas em


arrays:

 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).

8.1. Declaração e Inicialização de Vetores

No node.js, arrays são tratados como vetores de vetores. Ou seja, um array é um


vetor onde seus elementos também são vetores.

Complicado? A notação de inicialização de arrays vai nos ajudar a compreender


melhor esse conceito.

Assim como discutimos no capítulo sobre vetores, os primeiros passos para


trabalharmos com arrays dentro de qualquer linguagem de programação são a
declaração e inicialização:

Exemplo 8.1 - Declaração de inicialização de arrays de duas dimensões.

1 let arrayDeNumeros = [ // vetor de três linhas por quatro


colunas

2 [1, 8, 2, 0], // notem como cada linha é uma array em si

Capítulo 8: Arrays
Lógica de Programação com node.js

3 [7, 3, -2, 14],

4 [4, 1, 4, -8]

5 ]

7 let arrayHibrido = [ // vetor com elementos de tipos distintos

8 [false, "8", 0],

9 ["bola", 3.9, true],

10 [4, "carro", false]

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.

Por exemplo, os elementos da variável arrayDeNumeros são indexados da


seguinte forma:

linha\coluna 0 1 2 3

0 1 8 2 0

1 7 3 -2 14

2 4 1 4 -8

Tabela 8.3 - Indexação dos dados guardados na variável arrayDeNumeros.

Na variável arrayDeNumeros o valor presente na linha 2, coluna 0 é 4. Já o valor


presente na linha 1, coluna 3 é 14. A mesma lógica vale para qualquer outro elemento
do array e será utilizada na próxima seção do capítulo para vermos como acessar e
modificar os elementos de um array.

8.2. Acessando e Editando Arrays

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:

Exemplo 8.2 - Acesso aos elementos de um array.

1 let arrayDeNumeros = [ // vetor de três linhas por quatro


colunas

2 [1, 8, 2, 0],

3 [7, 3, -2, 14],

4 [4, 1, 4, -8]

5 ]

7 let elemento = arrayDeNumeros[1][2] // elemento = -2

8 elemento = arrayDeNumeros[0][0] // elemento = 1

9 elemento = arrayDeNumeros[2][3] // elemento = -8

Em arrays bidimensionais, o acesso aos elementos é feito através de uma notação


onde descrevemos qual a linha e qual a coluna que queremos acessar dentro de
colchetes:

variavel[linha, coluna]

É importante ressaltar que no exemplo 8.2 apenas os valores inclusivos entre 0 e 3


são válidos para acesso às colunas do array e apenas os valores inclusivos entre 0 e 2
são válidos para as linhas do array.

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:

Exemplo 8.3 - Edição de elementos de um array.

1 let arrayDeNumeros = [ // vetor de três linhas por quatro


colunas

Capítulo 8: Arrays
Lógica de Programação com node.js

2 [1, 8, 2, 0],

3 [7, 3, -2, 14],

4 [4, 1, 4, -8]

5 ]

7 arrayDeNumeros[1][2] = 7 // altera o valor de -2 para 7

8 arrayDeNumeros[0][0] = 4 // altera o valor de 1 para 4

9 arrayDeNumeros[2][3] = -1 // altera o valor de -8 para -1

Na linha 7 do exemplo acima, o valor da linha 1, coluna 2 da variável


arrayDeNumeros é alterado saindo do valor original que era -2 para 7. A mesma lógica
pode ser aplicada para as linhas 8 e 9 do exemplo.

8.3. Tamanho de um Array

Um array possui três tamanhos distintos:

 Quantidade de linhas: quantas linhas o array possui;


 Quantidade de colunas: quantas colunas o array possui, cada linha pode ter
uma quantidade de elementos distintos no node.js;
 Quantidade de elementos: quantos elementos o array possui no total.

Assim como nos vetores, podemos descobrir os tamanhos de um array através da


propriedade length:

Exemplo 8.4 - Tamanhos de um vetor.

1 let arrayDeNumeros = [

2 [1, 8, 2, 0],

3 [7, 3, -2], // esta linha foi alterada para conter 3


elementos

Capítulo 8: Arrays
Lógica de Programação com node.js

4 [4, 1, 4, -8]

5 ]

7 let linhas = arrayDeNumeros.length // = 3

8 let colunasDaLinhaZero = arrayDeNumeros[0].length // linhas = 4

9 let colunasDaLinhaUm = arrayDeNumeros[1].length // linhas = 3

10 let colunasDaLinhaDois = arrayDeNumeros[2].length // linhas = 4

11 let totalDeElementos = colunasDaLinhaZero + colunasDaLinhaUm +


colunasDaLinhaDois // número total de elementos

No exemplo acima, a propriedade length de um array de duas dimensões nos


retorna como resultado a quantidade de linhas do array (ver linha 7).

Já as linhas arrayDeNumeros[linha].length retornam como resultado o número


de colunas de uma determinada linha (ver linhas 8, 9 e 10). Ou seja, retornam o número
de elementos que cada linha possui.

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).

8.4. Arrays e Laços

Assim como nos vetores, a utilização de laços em conjunto com a propriedade


length nos permite visitar todos os elementos de um array de duas dimensões.

Consequentemente, podemos resolver de forma simples problemas que até então


eram bem complicados como:

 Como computar a média da turma inteira em um semestre?


 Como computar as melhores notas de cada matéria?
 Como computar a média de um único estudante?
 Como computar a média de uma única matéria?

Como exemplo, vamos escolher o problema da média dos números dentro de um


array (problema análogo à computar a média das turmas).

Capítulo 8: Arrays
Lógica de Programação com node.js

Exemplo 8.5 - Computa a média dos elementos de um array de duas dimensões.

1 let arrayDeNumeros = [

2 [1, 8, 2, 0],

3 [7, 3, -2], // qual o impacto dessa linha nos laços


abaixo?

4 [4, 1, 4, -8]

5 ]

7 let media = 0 // guardará a média ao final do código

8 let totalDeElementos = 0 // por que precisamos desta variável?

10 for (let i = 0; i < arrayDeNumeros.length; i++) {

11 for (let j = 0; j < arrayDeNumeros[i].length; j++) {

12 media = media + arrayDeNumeros[i][j]

13 totalDeElementos++ // não haveria uma forma mais


simples?

14 }

15 }

16

17 media = media / totalDeElementos

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.

Primeiramente, vamos compreender como o laço das linhas 11 até a 14 funciona.


Esse laço é responsável por visitar todos os elementos de uma determinada linha. No
exemplo 8.5, a linha visitada é linha do índice i. Ou seja, dada uma linha qualquer, o
laço visita todos os elementos dessa linha no array.

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

Assim, no exemplo 8.5, os laços trabalham em conjunto para que todos os


elementos de todas as linhas do array sejam visitadas.

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.

No exemplo 8.5, utilizamos um for para visitar os elementos de um array.


Entretanto, também poderíamos utilizar um while, um do ... while ou um for … in.

Por exemplo, se poderíamos utilizar um for … in para implementar uma solução


para o mesmo problema:

Exemplo 8.6 - Computa a média dos elementos de um array com o foreach.

1 let arrayDeNumeros = [

2 [1, 8, 2, 0],

3 [7, 3, -2], // qual o impacto dessa linha nos laços


abaixo?

4 [4, 1, 4, -8]

5 ]

7 let media = 0 // guardará a média ao final do código

8 let totalDeElementos = 0 // por que precisamos desta variável?

10 for (linha in arrayDeNumeros) { // para cada linha no array

11 for (coluna in arrayDeNumeros[linha]) { // para cada


elemento

12 media = media + arrayDeNumeros[linha][coluna]

13 totalDeElementos++ // não haveria uma forma mais


simples?

14 }

15 }

16

Capítulo 8: Arrays
Lógica de Programação com node.js

17 media = media / totalDeElementos

O exemplo 8.6 é equivalente ao exemplo 8.5, na prática, ao utilizarmos dois laços


do tipo for … in o interpretador do node.js cria os contadores e testa as condições de
parada durante a execução do código.

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.

Ao final do programa, todos os elementos do array são visitados e a média é


computada na linha 17.

8.5. Arrays e Subprogramas

Também podemos atualizar arrays em conjunto com subprogramas. Ou seja,


arrays podem ser passados como parâmetros, podem ser usados como variáveis
locais e também como retorno em subprogramas.

O exemplo abaixo demonstra como poderíamos utilizar um subprograma para


computar a média dos elementos de um array de duas dimensões:

Exemplo 8.7 - Computa a média dos elementos de um array de duas dimensões em


um subprograma

1 // recebe uma array como parâmetro e retorna a sua média

2 function mediaArray(array) {

3 let media = 0 // guardará a média ao final do código

4 let totalDeElementos = 0 // por que precisamos desta


variável?

6 for (let i = 0; i < array.length; i++) {

7 for (let j = 0; j < array[i].length; j++) {

8 media = media + array[i][j]

9 totalDeElementos++ // não haveria uma forma mais


simples?

Capítulo 8: Arrays
Lógica de Programação com node.js

10 }

11 }

12

13 media = media / totalDeElementos

14 return media

15 }

16

17 let arrayDeNumeros = [

18 [1, 8, 2, 0],

19 [7, 3, -2],

20 [4, 1, 4, -8]

21 ]

22 // passa uma referência de arrayDeNumeros para a função


mediaArray

23 let resultado = mediaArray(arrayDeNumeros)

24 console.log(resultado)

Quando passamos arrays como parâmetros para subprogramas não estamos


passando uma cópia do array, mas sim uma referência para a variável original. Ou seja,
arrays são passados por referência. Dessa forma, quando alteramos um array dentro
de um subprograma, a variável original também é alterada.

8.6. Arrays de Três ou mais Dimensões

Capítulo 8: Arrays
Lógica de Programação com node.js

Imagem 8.1 – Exemplo de array de mais dimensões: cubo.

O conceito de array não é limitado apenas a duas dimensões. Na prática é possível


criarmos arrays de quantas dimensões quisermos.

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).

No node.js arrays de três dimensões são tratados como vetores de vetores de


vetores. Assim, o primeiro vetor representa a quantidade de linhas. Cada linha tem seu
próprio vetor representando a quantidade de colunas que cada linha possui. E, por fim,
cada elemento presente nas colunas (endereçado por uma linha e uma coluna)
também é composto por um vetor que representa a quantidade de páginas (a
profundidade) do elemento.

A manipulação de arrays de três dimensões é feita através da adição de um novo


par de colchetes [] para representar as três dimensões. Ou seja, para trabalharmos
com arrays de três dimensões precisamos usar três pares de colchetes da seguinte
forma: nomeDaVariavel[linha][coluna][página].

O exemplo abaixo mostra como podemos manipular uma array de 3 dimensões no


node.js:

Exemplo 8.8 - Criando e acessando um array de três dimensões

1 // começamos com um array vazio e vamos preenchê-lo com outros


arrays

2 let array3d = []

3 let tamanho = 10 // número de linhas, colunas e páginas do


array

Capítulo 8: Arrays
Lógica de Programação com node.js

5 // inicializa o array, note a presença de um laço para cada


dimensão

6 for (let i = 0; i < tamanho; i++) {

7 array3d[i] = [] // cada linha é um novo vetor

8 for (let j = 0; j < tamanho; j++) {

9 array3d[i][j] = [] // elemento da coluna também é um


vetor

10 for (let k = 0; k < tamanho; k++) {

11 array3d[i][j][k] = i + j + k // inicializa a célula

12 }

13 }

14 }

15

16 // exemplos de manipulação do array três dimensões

17 console.log(array3d[1][2][3]) // 6 (linha 1, coluna 2, página


3)

18 console.log(array3d[5][1][0]) // 6 (linha 5, coluna 1, página


0)

19 console.log(array3d[4][3][8]) // 15 (linha 4, coluna 3, página


8)

O exemplo 8.8 demonstra a inicialização de um array de 3 dimensões através de


três laços aninhados (linhas 6 a 14). Esse modelo de três laços também pode ser
usado para acessar todos os elementos do array em outros tipos de problemas. Ao
seu fim, o exemplo também mostra como acessar os dados do array (linhas 17 a 19)
através da tripla indexação ([linha][coluna][página]).

O conceito de array não para em apenas três dimensões, na verdade, podemos


criar arrays de quantas dimensões quisermos. Por exemplo, um array de quatro
dimensões passa a ser indexado usando quatro colchetes:
nomedaVariável[dimensão1][dimensão2][dimensão3][dimensão4]. Geometricamente,
um array de quatro dimensões pode ser compreendido como um hipercubo7.

7
https://pt.wikipedia.org/wiki/Hipercubo

Capítulo 8: Arrays
Lógica de Programação com node.js

Generalizando o conceito, podemos criar arrays de n dimensões. Para indexá-los


precisamos utilizar n colchetes:
nomedaVariável[dimensão1][dimensão2]...[dimensãoN-1][dimensãoN]. Contudo, se
você está usando arrays com muitas dimensões pode ser um sinal de que seu código
está mal escrito. Nesse caso, talvez seja necessário avaliar a possibilidade de usar
tipos compostos, o assunto do nosso próximo capítulo.

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.

Entretanto, não sabemos ainda como representar de forma adequada as entidades


do mundo real dentro de um programa.

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 exemplo, uma matéria dentro de um curso possui diversas informações


(atributos) como o nome, a ementa, o professor que a ministrará, o livro que será
adotado, etc:

Atributo Tipo Significado

Nome String Nome da matéria. Ex.: Inglês I

Professor String Nome do professor: Ex.: João Lira

Ementa String Assuntos a serem estudados

Livro String Nome do livro adotado

Carga Horária Number Valor em horas da matéria no semestre


Tabela 9.1 - Possíveis características de um tipo chamado matéria.

Tipos definidos pelo programador funcionam como uma coleção de informações


agrupadas dentro de um único tipo e com um nome pré-definido. Ou seja, são tipos
criados pelo próprio programador para representar entidades que não estão presentes
na linguagem originalmente.

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

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

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

Nome Lógica de Programação

Professor Allan Lima

Ementa Algoritmos, variáveis, tipos, operadores...

Livro Lógica de Programação com o node.js

Carga Horária 60 horas


Tabela 9.2 - Exemplo de uma variável do tipo matéria.

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.

Assim, uma função que realiza-se a atualização da ementa de uma matéria


precisaria receber como parâmetro a nova ementa e a matéria que será atualizada.

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?

9.1. Criando tipos

Para criar nossos próprios tipos, O node.js possui o conceito de classes.

Classes são conceitos originários da programação orientada a objetos. Mas neste


capítulo vamos utilizá-las para programar de forma imperativa (em uma edição futura

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

do livro, vamos tratar dos conceitos fundamentais de programação orientada à


objetos).

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.

Um retângulo possui duas informações básicas altura e largura:

Imagem 9.1 - Um retângulo e seus atributos.

Através dos dados de um Retangulo podemos criar operadores para computar a


área e o perímetro de uma variável 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):

Exemplo 9.1 - Declaração do tipo Retangulo.

1 class Retangulo { // início da declaração da classe Retangulo

2 constructor(base, altura) { // define quais são os


atributos

3 this.base = base // base -> base passada como parâmetro

4 this.altura = altura // altura -> altura passada como


parâmetro

5 }

6 }

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

8 let r1 = new Retangulo(10, 20) // variável do tipo Retangulo

9 let r2 = new Retangulo(3, 4) // outra variável do tipo


Retangulo

10

11 console.log(r1.base, r1.altura) // imprime os dados de r1

12 r1.base = 15 // altera a base de r1

13 console.log(r1.base, r1.altura) // imprime os dados de r1


novamente

14

15 console.log(r2.base, r2.altura) // imprime os dados de r2

16 r2.altura = 5 // altera a altura de r2

17 console.log(r2.base, r2.altura) // imprime os dados de r2


novamente

O primeiro passo para compreender o exemplo 9.1 é a declaração da classe


Retangulo. Ela ocorre entre as linhas 1 e 6 do código.

A declaração de uma classe começa com a palavra reservada class seguida do


nome da classe e das chaves que abrem o seu conteúdo.

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).

Após a definição do tipo, já é possível criar variáveis do tipo Retangulo utilizando o


construtor que nós criamos e a palavra reservada new. As linhas 8 e 9 do código fazem
exatamente isso. Dessa forma, as variáveis r1 e r2 são retângulos, cada uma com
seus próprios valores para a base e altura, conforme o que foi passado para o
construtor. É comum dizermos que r1 e r2 são instâncias da classe Retangulo.

Para manipular os atributos de uma classe utilizamos o operador do ponto. Assim,


é possível observar, entre as linhas 11 e 16, como os valores da base e da altura são
lidos e alterados no código.

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

9.2. Criando operações sobre tipos

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.

No nosso exemplo, as operações mais simples que poderíamos implementar para


o tipo Retangulo conforme já discutimos seriam a computação do perímetro (soma
dos lados) e da área (base x altura) de um retângulo.

Exemplo 9.2 - Implementação da classe Retangulo e suas operações.

1 class Retangulo { // início da declaração da classe Retangulo

2 constructor(base, altura) { // define quais são os


atributos

3 this.base = base // base -> base passada como parâmetro

4 this.altura = altura // altura -> altura passada como


parâmetro

5 }

6 }

8 function perimetro(ret) { // computa o perímetro de um


Retangulo

9 return 2 * (ret.base + ret.altura)

10 }

11

12 function area(ret) { // computa a área de um Retangulo

13 return (ret.base * ret.altura)

14 }

15

16 let r1 = new Retangulo(10, 20) // variável do tipo Retangulo

17

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

18 let perimetro = perimetro(r1) // computa o perímetro de r1

19 console.log("perimetro de r1:", perimetro) // imprime o


perímetro

20

21 let area = area(r1) // computa a aŕea de r1

22 console.log("area de r1:", area) // imprime a área de r1

As principais novidades presentes no exemplo 9.2 são as funções perimetro


(linhas 8 a 10) e area (linhas 12 a 14) que recebem variáveis do tipo Retangulo
como parâmetro.

É através dessas funções que implementamos as operações de um tipo quando


programamos no paradigma imperativo.

Cada função representa uma operação distinta: a função perimetro computa o


perímetro de um Retangulo, já a função area computa a área de um Retangulo.

Como mostrado entre as linhas 18 e 21, é possível chamar as funções


implementadas e guardar seus resultados em variáveis. Porém, nem sempre é
necessário que uma função sobre uma classe retorne algo. Isso ocorre porque classes
são passadas como referência e toda vez que alteramos os atributos de uma classe
dentro de uma função, alteramos também a variável original.

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.

9.3. Relação entre Classes, Arrays e Strings

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:

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

Exemplo 9.3 - Implementação das classes Estudante e Turma.

1 class Estudante { // início da declaração da classe Estudante

2 constructor(nome, matricula) { // define quais são os


atributos

3 this.nome = nome // nome será do tipo string

4 this.matricula = matricula // matricula será do tipo


string

5 }

6 }

8 class Turma { // início da declaração da classe Turma

9 constructor(semestre, estudantes) { // define quais são os


atributos

10 this.semestre = semestre // nome será do tipo string

11 this.estudantes = estudantes // vetor do tipo Estudante

12 }

13 }

14

15 let s1 = new Estudante("João", "2022.02IGIPI001")

16 let s2 = new Estudante("Maria", "2022.02IGIPI002")

17

18 let estudantes = [s1, s2] // vetor do tipo Estudante

19 let turma202202 = new Turma("2022.02", estudantes) // criação


da turma

20

21 let nome = turma202202.estudantes[0].nome // João

22 console.log("nome do estudante:", nome) // imprime João

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

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

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.

Ainda no exemplo, é possível observarmos a criação de objetos dos tipos


Estudante (as variáveis s1 e s2), do tipo vetor de Estudante (a variável estudantes)
e, por fim, a criação da variável turma202202 que é do tipo turma.

Também é possível acessar e alterar os atributos dos objetos no exemplo 9.3


através dos operadores dos colchetes e do ponto como visto na linha 21:

let nome = turma202202.estudantes[0].nome

A linha 21 do exemplo 9.3 começa com a atribuição do valor de uma expressão à


variável nome. A expressão começa com a variável turma202202, em seguida, acessa
o índice 0 do seu vetor de estudantes, para só então acessar o nome do estudante
("João" neste caso). Assim, após a execução da linha a variável nome possui a string
"João".

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").

Funcionalmente falando, ambos os tipos são equivalentes, mas normalmente


utilizamos apenas as strings primitivas (apesar do interpretador do node.js
provavelmente tratar ambos os tipos da mesma forma por debaixo dos panos).

9.4. Separando nosso código em arquivos

Quando desenvolvemos projetos grandes com dezenas ou centenas de milhares


de linhas de código não podemos deixar todo o código em um único arquivo. Isso
tornaria nosso código muito complicado de ser compreendido, prejudicando assim a
sua legibilidade.

Como forma de resolver esses problemas, dividimos o nosso código em vários


arquivos. E um dos critérios de separação é justamente quando temos classes. Ou
seja, cada classe normalmente tem seu próprio arquivo.

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

Os exemplos 9.4 e 9.5 mostram como separar nossas classes Estudante e Turma
em um arquivo para cada:

Exemplo 9.4 - Implementação Estudante em um arquivo.

1 class Estudante { // início da declaração da classe Estudante

2 constructor(nome, matricula) { // define quais são os


atributos

3 this.nome = nome // nome será do tipo string

4 this.matricula = matricula // matricula será do tipo


string

5 }

6 }

8 // exporta a classe Estudante com o nome Estudante, o mesmo


pode ser feito com funções

9 exports.Estudante = Estudante

No final do exemplo 9.4, a linha 9 exports.Estudante = Estudante contém uma


instrução especial para o interpretador do node.js exportar a classe
Estudante com o nome Estudante (poderíamos usar outro nome). É esta linha que
nos permitirá declarar objetos do tipo Estudante em outros arquivos, como ilustrado
no exemplo 9.5.

Exemplo 9.5 - Implementação Turma em um arquivo.

1 // importa a classe Estudante do arquivo Estudante.js, para


mais de um símbolo importado separamos os nomes por vírgula.

2 const {Estudante} = require('./Estudante.js')

4 class Turma { // início da declaração da classe Turma

5 constructor(semestre, estudantes) { // define quais são os


atributos

6 this.semestre = semestre // nome será do tipo string

Capítulo 9: Tipos Criados pelo Programador


Lógica de Programação com node.js

7 this.estudantes = estudantes // vetor do tipo Estudante

8 }

9 }

10

11 // notem como podemos criar livremente objetos do tipo


Estudante

12 let s1 = new Estudante("João", "2022.02IGIPI001")

13 let s2 = new Estudante("Maria", "2022.02IGIPI002")

14

15 let estudantes = [s1, s2] // vetor do tipo Estudante

16 let turma202202 = new Turma("2022.02", estudantes) // criação


da turma

17

18 let nome = turma202202.estudantes[0].nome // João

19 console.log("nome do estudante:", nome) // imprime João

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).

Capítulo 9: Tipos Criados pelo Programador


RECURSÃO
Recursão é um conceito matemático muito importante para a criação de
programas capazes de resolver de forma simples e prática problemas que
originalmente podem parecer bastante complexos.

Contudo, dominar o uso da recursão em programação é uma tarefa árdua e que


leva bastante tempo.

Além disso, programas recursivos apesar da simplicidade do código, tendem a ser


lentos e consumirem muita memória. Dessa forma, há momentos em que a solução
recursiva funciona apenas como uma ponte para compreensão do problema, sendo
necessário o posterior desenvolvimento de uma solução não recursiva.

Neste capítulo, vamos a construção de funções recursivas.

Funções Recursivas

Na matemática há diversos tipos de problemas recursivos, talvez o mais popular


seja a computação do fatorial de um número.

O fatorial de um número positivo n é multiplicação de todos os números entre 1 e


n.

Por exemplo, o fatorial de 5 pode ser computado da seguinte forma:

1 x 2 x 3 x 4 x 5 = 120

Matematicamente, o fatorial de um número inteiro positivo n é computado da


seguinte forma:

Se n for igual a 0 ou 1: fatorial(n) = 1


Caso contrário: fatorial(n) = n * fatorial(n-1)

Assim, quando n assume os valores de 0 ou 1 o valor do fatorial sempre é 1.


Porém, quando n assume outros valores, pela própria definição da função fatorial,
precisamos computar o fatorial do número n - 1. E aí justamente nesse ponto que
começa a recursão porque para encontrar o fatorial de n, precisamos do fatorial de n
- 1, para encontrar n - 1 precisamos encontrar o fatorial n - 2, e assim
sucessivamente até chegarmos em um número x onde n - x = 1.

Apêndice A: Funções Recursivas


Lógica de Programação com node.js

Ou seja, para computar fatorial(n), precisamos computar antes fatorial(n -


1), mas para computar fatorial(n - 1) precisamos computar fatorial(n - 2),
até que cheguemos em um número x de forma que n - x = 1, nesse ponto sabemos
que fatorial(n - x) = 1 e podemos parar de realizar chamadas recorrentes à
função fatorial.

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).

Vamos agora pensar em um programa capaz de computar o fatorial de um


número. Primeiramente vamos trabalhar com uma solução tradicional e depois vamos
utilizar uma função recursiva.

Exemplo 10.1 - Computando o fatorial de um número de forma iterativa.

1 function fatorial(n) { // declaração da função que computa o


fatorial

2 var resultado = 1 // começa com 1 por conta das


multiplicações

4 for (var i = n; i > 0; i--) { // i vai de n até 1

5 resultado = resultado * i

6 }

7 return resultado

8 }

10 var f = new fatorial(5) // computa o fatorial de 5

11

12 console.log("resultado:", f) // imprime o valor do fatorial

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

Apêndice A: Funções Recursivas


Lógica de Programação com node.js

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.

Ainda no laço, a multiplicação é feita na linha 5. Nela, cada valor de i é


multiplicado pelo valor acumulado no contador geram a seguinte sequência de
multiplicações: n * n-1 * n-2 * … * 1 que acaba por gerar o valor do fatorial de n.
Por fim, na linha 7 a função retorna o fatorial do número n.

Se compararmos o código escrito no exemplo 10.1 com a definição da função


fatorial no começo da seção, vamos observar que ambos são muito distintos. Na
prática, praticamente não há similaridade, são soluções distintas (uma
recursiva/matemática e outra iterativa) para o mesmo problema.

Porém, podemos utilizar o conceito de recursão dentro do nosso código para


definir nossas funções. O segredo é fazer nosso código segundo a mesma lógica da
definição matemática da função fatorial, ou seja: chamando a própria função para
computar o fatorial de n.

Exemplo 10.2 - Computando o fatorial de um número de forma recursiva.

1 function fatorialRecursivo(n) { // declaração da função

2 if (n == 0 || n == 1) { // casos base: n = 0 ou n = 1

3 return 1

4 }

6 return n * fatorialRecursivo(n - 1) // caso indutivo

7 }

9 var f = new fatorialRecursivo(5) // computa o fatorial de 5

10

11 console.log("resultado:", f) // imprime o valor do fatorial

No exemplo 10.2, a função fatorialRecursivo parece ser mais simples do que a


função do exemplo 10.1. Isto ocorre porque na função fatorialRecursivo não há
laços em seu código.

Apêndice A: Funções Recursivas


Lógica de Programação com node.js

O código da função fatorialRecursivo começa com uma execução condicional


para tratar o que vamos chamar aqui de casos base: quando n é 0 ou 1. Estes casos
podem ser tratados diretamente, uma vez que o resultado sempre é um. Assim, a linha
3 retorna o valor de um para os casos citados, encerrando assim a execução da
função.

Porém, a linha 6 é o que caracteriza a função fatorialRecursivo como de fato


recursiva. Nessa linha, é possível observar que a função retorna n *
fatorialRecursivo(n - 1). Ou seja, antes de retornar o valor do fatorial a função
chama a si mesma, porém com um valor de n - 1, diferente da chamada anterior que
foi feita com n.

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.

Por exemplo, quando chamamos a função passando 5 como parâmetro, a seguinte


cadeira de chamadas é gerada:

fatorialRecursivo(5) | cai na linha 6, chama a função com 5 - 1


↳fatorialRecursivo(4) | cai na linha 6, chama a função com 4 -
1
↳fatorialRecursivo(3) | cai na linha 6, chama a função com
3 - 1
↳fatorialRecursivo(2) | cai na linha 6, chama a função
com 2 - 1
↳fatorialRecursivo(1) | cai na linha 3 (caso base),
retorna 1
fatorialRecursivo(2)↲ | retorna 2 * 1, ou seja 2
fatorialRecursivo(3)↲ | retorna 3 * 2, ou seja 6
fatorialRecursivo(4)↲ | retorna 4 * 6, ou seja 24
fatorialRecursivo(5)↲ | retorna 5 * 24, ou seja 120

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.

Algumas considerações importantes sobre funções recursivas

 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;

Apêndice A: Funções Recursivas


Lógica de Programação com node.js

 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.

Apêndice A: Funções Recursivas

Você também pode gostar